From 23a511dbe0f1f43c64de4f032503fcca48c4fd3b Mon Sep 17 00:00:00 2001 From: Sung Won Cho Date: Tue, 25 Jun 2019 19:20:19 +1000 Subject: [PATCH] Improve package structure (#207) * Improve package structure * Set up travis --- .travis.yml | 25 + Makefile | 56 + README.md | 2 +- cli/core/core.go | 358 ----- cli/core/queries.go | 56 - cli/infra/main.go | 225 --- cli/main.go | 80 - cli/main_test.go | 339 ---- cli/migrate/migrate_test.go | 1115 ------------- cli/testutils/fixtures/schema.sql | 50 - cli/testutils/main.go | 363 ----- cli/testutils/setup.go | 79 - pkg/assert/assert.go | 123 ++ {cli => pkg/cli}/.gitignore | 0 {cli => pkg/cli}/COMMANDS.md | 0 {cli => pkg/cli}/Gopkg.lock | 18 + {cli => pkg/cli}/Gopkg.toml | 0 {cli => pkg/cli}/Makefile | 0 {cli => pkg/cli}/README.md | 0 {cli => pkg/cli}/client/client.go | 34 +- {cli => pkg/cli}/cmd/add/add.go | 33 +- {cli => pkg/cli}/cmd/add/add_test.go | 4 +- {cli => pkg/cli}/cmd/cat/cat.go | 17 +- {cli => pkg/cli}/cmd/edit/edit.go | 17 +- {cli => pkg/cli}/cmd/find/find.go | 12 +- {cli => pkg/cli}/cmd/find/lexer.go | 0 {cli => pkg/cli}/cmd/find/lexer_test.go | 8 +- {cli => pkg/cli}/cmd/login/login.go | 30 +- {cli => pkg/cli}/cmd/logout/logout.go | 24 +- {cli => pkg/cli}/cmd/ls/ls.go | 14 +- {cli => pkg/cli}/cmd/remove/remove.go | 29 +- {cli => pkg/cli}/cmd/root/root.go | 45 +- {cli => pkg/cli}/cmd/sync/merge.go | 17 +- {cli => pkg/cli}/cmd/sync/merge_test.go | 4 +- {cli => pkg/cli}/cmd/sync/sync.go | 99 +- {cli => pkg/cli}/cmd/sync/sync_test.go | 1412 ++++++++--------- {cli => pkg/cli}/cmd/version/version.go | 4 +- {cli => pkg/cli}/cmd/view/view.go | 16 +- pkg/cli/consts/consts.go | 46 + pkg/cli/context/ctx.go | 36 + pkg/cli/context/operations.go | 48 + pkg/cli/context/testutils.go | 41 + cli/crypt/utils.go => pkg/cli/crypt/crypto.go | 2 - .../cli/crypt/crypto_test.go | 6 +- {cli/core => pkg/cli/database}/models.go | 19 +- {cli/core => pkg/cli/database}/models_test.go | 294 ++-- .../cli/database/queries.go | 90 +- .../cli/database/queries_test.go | 74 +- {cli/infra => pkg/cli/database}/sql.go | 42 +- pkg/cli/database/testutils.go | 170 ++ {cli => pkg/cli}/dnote-completion.bash | 0 {cli => pkg/cli}/dnote-completion.zsh | 0 pkg/cli/infra/config.go | 56 + pkg/cli/infra/init.go | 365 +++++ .../cli/infra/init_test.go | 38 +- {cli => pkg/cli}/install.sh | 0 {cli => pkg/cli}/log/log.go | 0 pkg/cli/main.go | 71 + pkg/cli/main_test.go | 333 ++++ .../migrate/fixtures/legacy-2-pre-dnote.json | 0 .../migrate/fixtures/legacy-3-pre-dnote.json | 0 .../fixtures/legacy-4-pre-dnoterc.yaml | 0 .../fixtures/legacy-5-post-actions.json | 0 .../fixtures/legacy-5-pre-actions.json | 0 .../migrate/fixtures/legacy-6-post-dnote.json | 0 .../migrate/fixtures/legacy-6-pre-dnote.json | 0 .../fixtures/legacy-7-post-actions.json | 0 .../fixtures/legacy-7-pre-actions.json | 0 .../migrate/fixtures/legacy-8-actions.json | 0 .../cli}/migrate/fixtures/legacy-8-dnote.json | 0 .../migrate/fixtures/legacy-8-dnoterc.yaml | 0 .../migrate/fixtures/legacy-8-schema.yaml | 0 .../migrate/fixtures/legacy-8-timestamps.yaml | 0 .../migrate/fixtures/local-1-pre-schema.sql | 0 .../migrate/fixtures/local-10-pre-schema.sql | 0 .../migrate/fixtures/local-11-pre-schema.sql | 0 .../migrate/fixtures/local-5-pre-schema.sql | 0 .../migrate/fixtures/local-7-pre-schema.sql | 0 .../migrate/fixtures/local-8-pre-schema.sql | 0 .../migrate/fixtures/local-9-pre-schema.sql | 0 .../migrate/fixtures/remote-1-pre-schema.sql | 0 {cli => pkg/cli}/migrate/legacy.go | 46 +- {cli => pkg/cli}/migrate/legacy_test.go | 319 ++-- {cli => pkg/cli}/migrate/migrate.go | 19 +- pkg/cli/migrate/migrate_test.go | 1134 +++++++++++++ {cli => pkg/cli}/migrate/migrations.go | 33 +- {cli/core => pkg/cli/output}/output.go | 11 +- {cli => pkg/cli}/scripts/build.sh | 2 +- {cli => pkg/cli}/scripts/dev.sh | 2 +- {cli => pkg/cli}/scripts/dump_schema.sh | 0 {cli => pkg/cli}/scripts/release.sh | 0 {cli => pkg/cli}/scripts/test.sh | 2 +- pkg/cli/testutils/main.go | 228 +++ pkg/cli/testutils/setup.go | 71 + pkg/cli/ui/editor.go | 146 ++ pkg/cli/ui/terminal.go | 97 ++ {cli/core => pkg/cli/upgrade}/upgrade.go | 35 +- {cli => pkg/cli}/utils/diff/diff.go | 0 {cli => pkg/cli}/utils/diff/diff_test.go | 4 +- cli/utils/utils.go => pkg/cli/utils/files.go | 108 +- pkg/cli/utils/utils.go | 42 + {server => pkg/server}/.gitignore | 0 {server => pkg/server}/Gopkg.lock | 11 + {server => pkg/server}/Gopkg.toml | 0 {server => pkg/server}/README.md | 0 {server => pkg/server}/api/.env.dev | 0 {server => pkg/server}/api/.env.test | 2 +- {server => pkg/server}/api/.gitignore | 0 {server => pkg/server}/api/README.md | 0 {server => pkg/server}/api/clock/clock.go | 0 {server => pkg/server}/api/crypt/crypt.go | 0 {server => pkg/server}/api/handlers/auth.go | 6 +- .../server}/api/handlers/digests.go | 8 +- {server => pkg/server}/api/handlers/health.go | 0 .../server}/api/handlers/helpers.go | 4 +- {server => pkg/server}/api/handlers/limit.go | 0 {server => pkg/server}/api/handlers/notes.go | 6 +- {server => pkg/server}/api/handlers/routes.go | 8 +- .../server}/api/handlers/routes_test.go | 25 +- {server => pkg/server}/api/handlers/semver.go | 0 .../server}/api/handlers/subscription.go | 6 +- {server => pkg/server}/api/handlers/user.go | 10 +- .../server}/api/handlers/v1_auth.go | 6 +- .../server}/api/handlers/v1_books.go | 8 +- .../server}/api/handlers/v1_notes.go | 8 +- .../server}/api/handlers/v1_sync.go | 4 +- .../server}/api/handlers/v1_sync_test.go | 8 +- .../server}/api/handlers/v2_books.go | 8 +- .../server}/api/handlers/v2_notes.go | 8 +- {server => pkg/server}/api/helpers/const.go | 0 {server => pkg/server}/api/helpers/helpers.go | 2 +- {server => pkg/server}/api/logger/main.go | 0 {server => pkg/server}/api/main.go | 10 +- .../server}/api/operations/books.go | 6 +- .../server}/api/operations/books_test.go | 65 +- .../server}/api/operations/helpers.go | 2 +- .../server}/api/operations/helpers_test.go | 9 +- .../server}/api/operations/notes.go | 6 +- .../server}/api/operations/notes_test.go | 63 +- .../server}/api/operations/sessions.go | 4 +- .../server}/api/operations/subscriptions.go | 2 +- .../server}/api/operations/users.go | 4 +- .../server}/api/presenters/presenters.go | 2 +- {server => pkg/server}/api/scripts/dev.sh | 2 +- .../api/scripts/makeDemoDigests/main.go | 4 +- {server => pkg/server}/api/scripts/setup.sh | 0 .../server}/api/scripts/test-local.sh | 2 +- {server => pkg/server}/api/scripts/test.sh | 2 +- {server => pkg/server}/database/main.go | 0 {server => pkg/server}/database/models.go | 0 {server => pkg/server}/database/types.go | 0 {server => pkg/server}/job/.env.dev | 0 {server => pkg/server}/job/.gitignore | 0 {server => pkg/server}/job/digest/digest.go | 4 +- {server => pkg/server}/job/main.go | 6 +- {server => pkg/server}/job/scripts/dev.sh | 0 {server => pkg/server}/mailer/mailer.go | 0 .../server}/mailer/templates/.env.dev | 0 .../server}/mailer/templates/.gitignore | 0 .../server}/mailer/templates/README.md | 0 .../server}/mailer/templates/main.go | 6 +- .../server}/mailer/templates/scripts/run.sh | 0 .../templates/src/email_verification.html | 0 .../server}/mailer/templates/src/footer.html | 0 .../server}/mailer/templates/src/header.html | 0 .../mailer/templates/src/reset_password.html | 0 .../mailer/templates/src/weekly_digest.html | 0 {server => pkg/server}/mailer/types.go | 0 {server => pkg/server}/mailer/utils.go | 2 +- {server => pkg/server}/testutils/main.go | 129 +- scripts/license.sh | 23 +- 171 files changed, 4724 insertions(+), 4505 deletions(-) create mode 100644 .travis.yml create mode 100644 Makefile delete mode 100644 cli/core/core.go delete mode 100644 cli/core/queries.go delete mode 100644 cli/infra/main.go delete mode 100644 cli/main.go delete mode 100644 cli/main_test.go delete mode 100644 cli/migrate/migrate_test.go delete mode 100644 cli/testutils/fixtures/schema.sql delete mode 100644 cli/testutils/main.go delete mode 100644 cli/testutils/setup.go create mode 100644 pkg/assert/assert.go rename {cli => pkg/cli}/.gitignore (100%) rename {cli => pkg/cli}/COMMANDS.md (100%) rename {cli => pkg/cli}/Gopkg.lock (87%) rename {cli => pkg/cli}/Gopkg.toml (100%) rename {cli => pkg/cli}/Makefile (100%) rename {cli => pkg/cli}/README.md (100%) rename {cli => pkg/cli}/client/client.go (91%) rename {cli => pkg/cli}/cmd/add/add.go (81%) rename {cli => pkg/cli}/cmd/add/add_test.go (92%) rename {cli => pkg/cli}/cmd/cat/cat.go (82%) rename {cli => pkg/cli}/cmd/edit/edit.go (88%) rename {cli => pkg/cli}/cmd/find/find.go (93%) rename {cli => pkg/cli}/cmd/find/lexer.go (100%) rename {cli => pkg/cli}/cmd/find/lexer_test.go (94%) rename {cli => pkg/cli}/cmd/login/login.go (74%) rename {cli => pkg/cli}/cmd/logout/logout.go (74%) rename {cli => pkg/cli}/cmd/ls/ls.go (92%) rename {cli => pkg/cli}/cmd/remove/remove.go (80%) rename {cli => pkg/cli}/cmd/root/root.go (58%) rename {cli => pkg/cli}/cmd/sync/merge.go (89%) rename {cli => pkg/cli}/cmd/sync/merge_test.go (94%) rename {cli => pkg/cli}/cmd/sync/sync.go (88%) rename {cli => pkg/cli}/cmd/sync/sync_test.go (50%) rename {cli => pkg/cli}/cmd/version/version.go (92%) rename {cli => pkg/cli}/cmd/view/view.go (86%) create mode 100644 pkg/cli/consts/consts.go create mode 100644 pkg/cli/context/ctx.go create mode 100644 pkg/cli/context/operations.go create mode 100644 pkg/cli/context/testutils.go rename cli/crypt/utils.go => pkg/cli/crypt/crypto.go (97%) rename cli/crypt/utils_test.go => pkg/cli/crypt/crypto_test.go (93%) rename {cli/core => pkg/cli/database}/models.go (90%) rename {cli/core => pkg/cli/database}/models_test.go (55%) rename cli/core/operations.go => pkg/cli/database/queries.go (59%) rename cli/core/operations_test.go => pkg/cli/database/queries_test.go (62%) rename {cli/infra => pkg/cli/database}/sql.go (90%) create mode 100644 pkg/cli/database/testutils.go rename {cli => pkg/cli}/dnote-completion.bash (100%) rename {cli => pkg/cli}/dnote-completion.zsh (100%) create mode 100644 pkg/cli/infra/config.go create mode 100644 pkg/cli/infra/init.go rename cli/core/core_test.go => pkg/cli/infra/init_test.go (57%) rename {cli => pkg/cli}/install.sh (100%) rename {cli => pkg/cli}/log/log.go (100%) create mode 100644 pkg/cli/main.go create mode 100644 pkg/cli/main_test.go rename {cli => pkg/cli}/migrate/fixtures/legacy-2-pre-dnote.json (100%) rename {cli => pkg/cli}/migrate/fixtures/legacy-3-pre-dnote.json (100%) rename {cli => pkg/cli}/migrate/fixtures/legacy-4-pre-dnoterc.yaml (100%) rename {cli => pkg/cli}/migrate/fixtures/legacy-5-post-actions.json (100%) rename {cli => pkg/cli}/migrate/fixtures/legacy-5-pre-actions.json (100%) rename {cli => pkg/cli}/migrate/fixtures/legacy-6-post-dnote.json (100%) rename {cli => pkg/cli}/migrate/fixtures/legacy-6-pre-dnote.json (100%) rename {cli => pkg/cli}/migrate/fixtures/legacy-7-post-actions.json (100%) rename {cli => pkg/cli}/migrate/fixtures/legacy-7-pre-actions.json (100%) rename {cli => pkg/cli}/migrate/fixtures/legacy-8-actions.json (100%) rename {cli => pkg/cli}/migrate/fixtures/legacy-8-dnote.json (100%) rename {cli => pkg/cli}/migrate/fixtures/legacy-8-dnoterc.yaml (100%) rename {cli => pkg/cli}/migrate/fixtures/legacy-8-schema.yaml (100%) rename {cli => pkg/cli}/migrate/fixtures/legacy-8-timestamps.yaml (100%) rename {cli => pkg/cli}/migrate/fixtures/local-1-pre-schema.sql (100%) rename {cli => pkg/cli}/migrate/fixtures/local-10-pre-schema.sql (100%) rename {cli => pkg/cli}/migrate/fixtures/local-11-pre-schema.sql (100%) rename {cli => pkg/cli}/migrate/fixtures/local-5-pre-schema.sql (100%) rename {cli => pkg/cli}/migrate/fixtures/local-7-pre-schema.sql (100%) rename {cli => pkg/cli}/migrate/fixtures/local-8-pre-schema.sql (100%) rename {cli => pkg/cli}/migrate/fixtures/local-9-pre-schema.sql (100%) rename {cli => pkg/cli}/migrate/fixtures/remote-1-pre-schema.sql (100%) rename {cli => pkg/cli}/migrate/legacy.go (95%) rename {cli => pkg/cli}/migrate/legacy_test.go (53%) rename {cli => pkg/cli}/migrate/migrate.go (84%) create mode 100644 pkg/cli/migrate/migrate_test.go rename {cli => pkg/cli}/migrate/migrations.go (94%) rename {cli/core => pkg/cli/output}/output.go (82%) rename {cli => pkg/cli}/scripts/build.sh (97%) rename {cli => pkg/cli}/scripts/dev.sh (81%) rename {cli => pkg/cli}/scripts/dump_schema.sh (100%) rename {cli => pkg/cli}/scripts/release.sh (100%) rename {cli => pkg/cli}/scripts/test.sh (85%) create mode 100644 pkg/cli/testutils/main.go create mode 100644 pkg/cli/testutils/setup.go create mode 100644 pkg/cli/ui/editor.go create mode 100644 pkg/cli/ui/terminal.go rename {cli/core => pkg/cli/upgrade}/upgrade.go (71%) rename {cli => pkg/cli}/utils/diff/diff.go (100%) rename {cli => pkg/cli}/utils/diff/diff_test.go (95%) rename cli/utils/utils.go => pkg/cli/utils/files.go (55%) create mode 100644 pkg/cli/utils/utils.go rename {server => pkg/server}/.gitignore (100%) rename {server => pkg/server}/Gopkg.lock (95%) rename {server => pkg/server}/Gopkg.toml (100%) rename {server => pkg/server}/README.md (100%) rename {server => pkg/server}/api/.env.dev (100%) rename {server => pkg/server}/api/.env.test (95%) rename {server => pkg/server}/api/.gitignore (100%) rename {server => pkg/server}/api/README.md (100%) rename {server => pkg/server}/api/clock/clock.go (100%) rename {server => pkg/server}/api/crypt/crypt.go (100%) rename {server => pkg/server}/api/handlers/auth.go (98%) rename {server => pkg/server}/api/handlers/digests.go (95%) rename {server => pkg/server}/api/handlers/health.go (100%) rename {server => pkg/server}/api/handlers/helpers.go (96%) rename {server => pkg/server}/api/handlers/limit.go (100%) rename {server => pkg/server}/api/handlers/notes.go (98%) rename {server => pkg/server}/api/handlers/routes.go (98%) rename {server => pkg/server}/api/handlers/routes_test.go (90%) rename {server => pkg/server}/api/handlers/semver.go (100%) rename {server => pkg/server}/api/handlers/subscription.go (98%) rename {server => pkg/server}/api/handlers/user.go (98%) rename {server => pkg/server}/api/handlers/v1_auth.go (98%) rename {server => pkg/server}/api/handlers/v1_books.go (97%) rename {server => pkg/server}/api/handlers/v1_notes.go (95%) rename {server => pkg/server}/api/handlers/v1_sync.go (98%) rename {server => pkg/server}/api/handlers/v1_sync_test.go (84%) rename {server => pkg/server}/api/handlers/v2_books.go (93%) rename {server => pkg/server}/api/handlers/v2_notes.go (93%) rename {server => pkg/server}/api/helpers/const.go (100%) rename {server => pkg/server}/api/helpers/helpers.go (96%) rename {server => pkg/server}/api/logger/main.go (100%) rename {server => pkg/server}/api/main.go (91%) rename {server => pkg/server}/api/operations/books.go (95%) rename {server => pkg/server}/api/operations/books_test.go (69%) rename {server => pkg/server}/api/operations/helpers.go (96%) rename {server => pkg/server}/api/operations/helpers_test.go (83%) rename {server => pkg/server}/api/operations/notes.go (95%) rename {server => pkg/server}/api/operations/notes_test.go (74%) rename {server => pkg/server}/api/operations/sessions.go (95%) rename {server => pkg/server}/api/operations/subscriptions.go (98%) rename {server => pkg/server}/api/operations/users.go (97%) rename {server => pkg/server}/api/presenters/presenters.go (98%) rename {server => pkg/server}/api/scripts/dev.sh (86%) rename {server => pkg/server}/api/scripts/makeDemoDigests/main.go (98%) rename {server => pkg/server}/api/scripts/setup.sh (100%) rename {server => pkg/server}/api/scripts/test-local.sh (71%) rename {server => pkg/server}/api/scripts/test.sh (75%) rename {server => pkg/server}/database/main.go (100%) rename {server => pkg/server}/database/models.go (100%) rename {server => pkg/server}/database/types.go (100%) rename {server => pkg/server}/job/.env.dev (100%) rename {server => pkg/server}/job/.gitignore (100%) rename {server => pkg/server}/job/digest/digest.go (97%) rename {server => pkg/server}/job/main.go (92%) rename {server => pkg/server}/job/scripts/dev.sh (100%) rename {server => pkg/server}/mailer/mailer.go (100%) rename {server => pkg/server}/mailer/templates/.env.dev (100%) rename {server => pkg/server}/mailer/templates/.gitignore (100%) rename {server => pkg/server}/mailer/templates/README.md (100%) rename {server => pkg/server}/mailer/templates/main.go (94%) rename {server => pkg/server}/mailer/templates/scripts/run.sh (100%) rename {server => pkg/server}/mailer/templates/src/email_verification.html (100%) rename {server => pkg/server}/mailer/templates/src/footer.html (100%) rename {server => pkg/server}/mailer/templates/src/header.html (100%) rename {server => pkg/server}/mailer/templates/src/reset_password.html (100%) rename {server => pkg/server}/mailer/templates/src/weekly_digest.html (100%) rename {server => pkg/server}/mailer/types.go (100%) rename {server => pkg/server}/mailer/utils.go (97%) rename {server => pkg/server}/testutils/main.go (68%) diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..45dc303a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +language: go +dist: xenial + +go: + - 1.12 + +addons: + postgresql: "10" + +env: + - NODE_VERSION=10.15.0 + +before_script: + - nvm install "$NODE_VERSION" + - nvm use "$NODE_VERSION" + - node --version + - psql -c "CREATE DATABASE dnote_test;" -U postgres + +install: + - make install + +script: + - make test-cli + - make test-api + - make test-web diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..a247cf8c --- /dev/null +++ b/Makefile @@ -0,0 +1,56 @@ +DEP := $(shell command -v dep 2> /dev/null) +NPM := $(shell command -v npm 2> /dev/null) + +## installation +install-cli: +ifndef DEP + @echo "==> installing dep" + @curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh +endif + + @echo "==> installing CLI dependencies" + @(cd ${GOPATH}/src/github.com/dnote/dnote/pkg/cli && dep ensure) +.PHONY: install-cli + +install-web: +ifndef NPM + @echo "npm not found" + exit 1 +endif + + @echo "==> installing web dependencies" + @(cd ${GOPATH}/src/github.com/dnote/dnote/web && npm install) +.PHONY: install-web + +install-server: + @echo "==> installing server dependencies" + @(cd ${GOPATH}/src/github.com/dnote/dnote/pkg/server && dep ensure) +.PHONY: install-server + +install: install-cli install-web install-server +.PHONY: install + +## test +test-cli: + @echo "==> running CLI test" + @${GOPATH}/src/github.com/dnote/dnote/pkg/cli/scripts/test.sh +.PHONY: test-cli + +test-api: + @echo "==> running API test" + @${GOPATH}/src/github.com/dnote/dnote/pkg/server/api/scripts/test-local.sh +.PHONY: test-api + +test-web: + @echo "==> running web test" + @(cd ${GOPATH}/src/github.com/dnote/dnote/web && npm run test) +.PHONY: test-web + +test: test-cli test-api test-web +.PHONY: test + +## build +build-web: + @echo "==> building web" + @${GOPATH}/src/github.com/dnote/dnote/web/scripts/build-prod.sh +.PHONY: build-web diff --git a/README.md b/README.md index 477fd2a0..9dc24bbd 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Dnote is a simple notebook for developers. -[![Build Status](https://semaphoreci.com/api/v1/dnote/dnote-2/branches/master/badge.svg)](https://semaphoreci.com/dnote/dnote-2) +[![Build Status](https://travis-ci.org/dnote/dnote.svg?branch=master)](https://travis-ci.org/dnote/dnote) ## What is Dnote? diff --git a/cli/core/core.go b/cli/core/core.go deleted file mode 100644 index db33ddf6..00000000 --- a/cli/core/core.go +++ /dev/null @@ -1,358 +0,0 @@ -/* Copyright (C) 2019 Monomax Software Pty Ltd - * - * This file is part of Dnote CLI. - * - * Dnote CLI 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 CLI 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 CLI. If not, see . - */ - -package core - -import ( - "database/sql" - "fmt" - "io/ioutil" - "os" - "os/exec" - "strconv" - "strings" - "time" - - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" - "github.com/dnote/dnote/cli/utils" - "github.com/pkg/errors" - "github.com/satori/go.uuid" - "github.com/spf13/cobra" - "gopkg.in/yaml.v2" -) - -var ( - // ConfigFilename is the name of the config file - ConfigFilename = "dnoterc" - // TmpContentFilename is the name of the temporary file that holds editor input - TmpContentFilename = "DNOTE_TMPCONTENT.md" -) - -// RunEFunc is a function type of dnote commands -type RunEFunc func(*cobra.Command, []string) error - -// GetConfigPath returns the path to the dnote config file -func GetConfigPath(ctx infra.DnoteCtx) string { - return fmt.Sprintf("%s/%s", ctx.DnoteDir, ConfigFilename) -} - -// GetDnoteTmpContentPath returns the path to the temporary file containing -// content being added or edited -func GetDnoteTmpContentPath(ctx infra.DnoteCtx) string { - return fmt.Sprintf("%s/%s", ctx.DnoteDir, TmpContentFilename) -} - -// GetBookUUID returns a uuid of a book given a label -func GetBookUUID(ctx infra.DnoteCtx, label string) (string, error) { - db := ctx.DB - - var ret string - err := db.QueryRow("SELECT uuid FROM books WHERE label = ?", label).Scan(&ret) - if err == sql.ErrNoRows { - return ret, errors.Errorf("book '%s' not found", label) - } else if err != nil { - return ret, errors.Wrap(err, "querying the book") - } - - return ret, nil -} - -// getEditorCommand returns the system's editor command with appropriate flags, -// if necessary, to make the command wait until editor is close to exit. -func getEditorCommand() string { - editor := os.Getenv("EDITOR") - - var ret string - - switch editor { - case "atom": - ret = "atom -w" - case "subl": - ret = "subl -n -w" - case "mate": - ret = "mate -w" - case "vim": - ret = "vim" - case "nano": - ret = "nano" - case "emacs": - ret = "emacs" - case "nvim": - ret = "nvim" - default: - ret = "vi" - } - - return ret -} - -// InitFiles creates, if necessary, the dnote directory and files inside -func InitFiles(ctx infra.DnoteCtx) error { - if err := initDnoteDir(ctx); err != nil { - return errors.Wrap(err, "creating the dnote dir") - } - if err := initConfigFile(ctx); err != nil { - return errors.Wrap(err, "generating the config file") - } - - return nil -} - -// initConfigFile populates a new config file if it does not exist yet -func initConfigFile(ctx infra.DnoteCtx) error { - path := GetConfigPath(ctx) - - if utils.FileExists(path) { - return nil - } - - editor := getEditorCommand() - - config := infra.Config{ - Editor: editor, - } - - b, err := yaml.Marshal(config) - if err != nil { - return errors.Wrap(err, "marshalling config into YAML") - } - - err = ioutil.WriteFile(path, b, 0644) - if err != nil { - return errors.Wrap(err, "writing the config file") - } - - return nil -} - -// initDnoteDir initializes dnote directory if it does not exist yet -func initDnoteDir(ctx infra.DnoteCtx) error { - path := ctx.DnoteDir - - if utils.FileExists(path) { - return nil - } - - if err := os.MkdirAll(path, 0755); err != nil { - return errors.Wrap(err, "Failed to create dnote directory") - } - - return nil -} - -// WriteConfig writes the config to the config file -func WriteConfig(ctx infra.DnoteCtx, config infra.Config) error { - d, err := yaml.Marshal(config) - if err != nil { - return errors.Wrap(err, "marhsalling config") - } - - configPath := GetConfigPath(ctx) - - err = ioutil.WriteFile(configPath, d, 0644) - if err != nil { - errors.Wrap(err, "writing the config file") - } - - return nil -} - -// LogAction logs action and updates the last_action -func LogAction(tx *sql.Tx, schema int, actionType, data string, timestamp int64) error { - uuid := uuid.NewV4().String() - - _, err := tx.Exec(`INSERT INTO actions (uuid, schema, type, data, timestamp) - VALUES (?, ?, ?, ?, ?)`, uuid, schema, actionType, data, timestamp) - if err != nil { - return errors.Wrap(err, "inserting an action") - } - - _, err = tx.Exec("UPDATE system SET value = ? WHERE key = ?", timestamp, "last_action") - if err != nil { - return errors.Wrap(err, "updating last_action") - } - - return nil -} - -// ReadConfig reads the config file -func ReadConfig(ctx infra.DnoteCtx) (infra.Config, error) { - var ret infra.Config - - configPath := GetConfigPath(ctx) - b, err := ioutil.ReadFile(configPath) - if err != nil { - return ret, errors.Wrap(err, "reading config file") - } - - err = yaml.Unmarshal(b, &ret) - if err != nil { - return ret, errors.Wrap(err, "unmarshalling config") - } - - return ret, nil -} - -// SanitizeContent sanitizes note content -func SanitizeContent(s string) string { - var ret string - - ret = strings.Trim(s, " ") - - // Remove newline at the end of the file because POSIX defines a line as - // characters followed by a newline - ret = strings.TrimSuffix(ret, "\n") - ret = strings.TrimSuffix(ret, "\r\n") - - return ret -} - -func newEditorCmd(ctx infra.DnoteCtx, fpath string) (*exec.Cmd, error) { - config, err := ReadConfig(ctx) - if err != nil { - return nil, errors.Wrap(err, "reading config") - } - - args := strings.Fields(config.Editor) - args = append(args, fpath) - - return exec.Command(args[0], args[1:]...), nil -} - -// GetEditorInput gets the user input by launching a text editor and waiting for -// it to exit -func GetEditorInput(ctx infra.DnoteCtx, fpath string, content *string) error { - if !utils.FileExists(fpath) { - f, err := os.Create(fpath) - if err != nil { - return errors.Wrap(err, "creating a temporary content file") - } - err = f.Close() - if err != nil { - return errors.Wrap(err, "closing the temporary content file") - } - } - - cmd, err := newEditorCmd(ctx, fpath) - if err != nil { - return errors.Wrap(err, "creating an editor command") - } - - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - - err = cmd.Start() - if err != nil { - return errors.Wrapf(err, "launching an editor") - } - - err = cmd.Wait() - if err != nil { - return errors.Wrap(err, "waiting for the editor") - } - - b, err := ioutil.ReadFile(fpath) - if err != nil { - return errors.Wrap(err, "reading the temporary content file") - } - - err = os.Remove(fpath) - if err != nil { - return errors.Wrap(err, "removing the temporary content file") - } - - raw := string(b) - c := SanitizeContent(raw) - - *content = c - - return nil -} - -func initSystemKV(db *infra.DB, key string, val string) error { - var count int - if err := db.QueryRow("SELECT count(*) FROM system WHERE key = ?", key).Scan(&count); err != nil { - return errors.Wrapf(err, "counting %s", key) - } - - if count > 0 { - return nil - } - - if _, err := db.Exec("INSERT INTO system (key, value) VALUES (?, ?)", key, val); err != nil { - db.Rollback() - return errors.Wrapf(err, "inserting %s %s", key, val) - - } - - return nil -} - -// InitSystem inserts system data if missing -func InitSystem(ctx infra.DnoteCtx) error { - db := ctx.DB - - tx, err := db.Begin() - if err != nil { - return errors.Wrap(err, "beginning a transaction") - } - - nowStr := strconv.FormatInt(time.Now().Unix(), 10) - if err := initSystemKV(tx, infra.SystemLastUpgrade, nowStr); err != nil { - return errors.Wrapf(err, "initializing system config for %s", infra.SystemLastUpgrade) - } - if err := initSystemKV(tx, infra.SystemLastMaxUSN, "0"); err != nil { - return errors.Wrapf(err, "initializing system config for %s", infra.SystemLastMaxUSN) - } - if err := initSystemKV(tx, infra.SystemLastSyncAt, "0"); err != nil { - return errors.Wrapf(err, "initializing system config for %s", infra.SystemLastSyncAt) - } - - tx.Commit() - - return nil -} - -// GetValidSession returns a session key from the local storage if one exists and is not expired -// If one does not exist or is expired, it prints out an instruction and returns false -func GetValidSession(ctx infra.DnoteCtx) (string, bool, error) { - db := ctx.DB - - var sessionKey string - var sessionKeyExpires int64 - - if err := GetSystem(db, infra.SystemSessionKey, &sessionKey); err != nil { - return "", false, errors.Wrap(err, "getting session key") - } - if err := GetSystem(db, infra.SystemSessionKeyExpiry, &sessionKeyExpires); err != nil { - return "", false, errors.Wrap(err, "getting session key expiry") - } - - if sessionKey == "" { - log.Error("login required. please run `dnote login`\n") - return "", false, nil - } - if sessionKeyExpires < time.Now().Unix() { - log.Error("sesison expired. please run `dnote login`\n") - return "", false, nil - } - - return sessionKey, true, nil -} diff --git a/cli/core/queries.go b/cli/core/queries.go deleted file mode 100644 index cd50013f..00000000 --- a/cli/core/queries.go +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright (C) 2019 Monomax Software Pty Ltd - * - * This file is part of Dnote CLI. - * - * Dnote CLI 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 CLI 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 CLI. If not, see . - */ - -package core - -import ( - "database/sql" - - "github.com/dnote/dnote/cli/infra" - "github.com/pkg/errors" -) - -// NoteInfo is a basic information about a note -type NoteInfo struct { - RowID int - BookLabel string - UUID string - Content string - AddedOn int64 - EditedOn int64 -} - -// GetNoteInfo returns a NoteInfo for the note with the given noteRowID -func GetNoteInfo(ctx infra.DnoteCtx, noteRowID string) (NoteInfo, error) { - var ret NoteInfo - - db := ctx.DB - err := db.QueryRow(`SELECT books.label, notes.uuid, notes.body, notes.added_on, notes.edited_on, notes.rowid - FROM notes - INNER JOIN books ON books.uuid = notes.book_uuid - WHERE notes.rowid = ? AND notes.deleted = false`, noteRowID). - Scan(&ret.BookLabel, &ret.UUID, &ret.Content, &ret.AddedOn, &ret.EditedOn, &ret.RowID) - if err == sql.ErrNoRows { - return ret, errors.Errorf("note %s not found", noteRowID) - } else if err != nil { - return ret, errors.Wrap(err, "querying the note") - } - - return ret, nil - -} diff --git a/cli/infra/main.go b/cli/infra/main.go deleted file mode 100644 index c64179c3..00000000 --- a/cli/infra/main.go +++ /dev/null @@ -1,225 +0,0 @@ -/* Copyright (C) 2019 Monomax Software Pty Ltd - * - * This file is part of Dnote CLI. - * - * Dnote CLI 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 CLI 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 CLI. If not, see . - */ - -// Package infra defines dnote structure -package infra - -import ( - "database/sql" - "encoding/base64" - "fmt" - "os" - "os/user" - - // use sqlite - _ "github.com/mattn/go-sqlite3" - - "github.com/pkg/errors" -) - -var ( - // DnoteDirName is the name of the directory containing dnote files - DnoteDirName = ".dnote" - - // SystemSchema is the key for schema in the system table - SystemSchema = "schema" - // SystemRemoteSchema is the key for remote schema in the system table - SystemRemoteSchema = "remote_schema" - // SystemLastSyncAt is the timestamp of the server at the last sync - SystemLastSyncAt = "last_sync_time" - // SystemLastMaxUSN is the user's max_usn from the server at the alst sync - SystemLastMaxUSN = "last_max_usn" - // SystemLastUpgrade is the timestamp at which the system more recently checked for an upgrade - SystemLastUpgrade = "last_upgrade" - // SystemCipherKey is the encryption key - SystemCipherKey = "enc_key" - // SystemSessionKey is the session key - SystemSessionKey = "session_token" - // SystemSessionKeyExpiry is the timestamp at which the session key will expire - SystemSessionKeyExpiry = "session_token_expiry" -) - -// DnoteCtx is a context holding the information of the current runtime -type DnoteCtx struct { - HomeDir string - DnoteDir string - APIEndpoint string - Version string - DB *DB - SessionKey string - SessionKeyExpiry int64 - CipherKey []byte -} - -// Config holds dnote configuration -type Config struct { - Editor string -} - -// NewCtx returns a new dnote context -func NewCtx(apiEndpoint, versionTag string) (DnoteCtx, error) { - homeDir, err := getHomeDir() - if err != nil { - return DnoteCtx{}, errors.Wrap(err, "Failed to get home dir") - } - dnoteDir := getDnoteDir(homeDir) - - dnoteDBPath := fmt.Sprintf("%s/dnote.db", dnoteDir) - db, err := OpenDB(dnoteDBPath) - if err != nil { - return DnoteCtx{}, errors.Wrap(err, "conntecting to db") - } - - ret := DnoteCtx{ - HomeDir: homeDir, - DnoteDir: dnoteDir, - APIEndpoint: apiEndpoint, - Version: versionTag, - DB: db, - } - - return ret, nil -} - -// SetupCtx populates context and returns a new context -func SetupCtx(ctx DnoteCtx) (DnoteCtx, error) { - db := ctx.DB - - var sessionKey, cipherKeyB64 string - var sessionKeyExpiry int64 - - err := db.QueryRow("SELECT value FROM system WHERE key = ?", SystemSessionKey).Scan(&sessionKey) - if err != nil && err != sql.ErrNoRows { - return ctx, errors.Wrap(err, "finding sesison key") - } - err = db.QueryRow("SELECT value FROM system WHERE key = ?", SystemCipherKey).Scan(&cipherKeyB64) - if err != nil && err != sql.ErrNoRows { - return ctx, errors.Wrap(err, "finding sesison key") - } - err = db.QueryRow("SELECT value FROM system WHERE key = ?", SystemSessionKeyExpiry).Scan(&sessionKeyExpiry) - if err != nil && err != sql.ErrNoRows { - return ctx, errors.Wrap(err, "finding sesison key expiry") - } - - cipherKey, err := base64.StdEncoding.DecodeString(cipherKeyB64) - if err != nil { - return ctx, errors.Wrap(err, "decoding cipherKey from base64") - } - - ret := DnoteCtx{ - HomeDir: ctx.HomeDir, - DnoteDir: ctx.DnoteDir, - APIEndpoint: ctx.APIEndpoint, - Version: ctx.Version, - DB: ctx.DB, - SessionKey: sessionKey, - SessionKeyExpiry: sessionKeyExpiry, - CipherKey: cipherKey, - } - - return ret, nil -} - -func getDnoteDir(homeDir string) string { - var ret string - - dnoteDirEnv := os.Getenv("DNOTE_DIR") - if dnoteDirEnv == "" { - ret = fmt.Sprintf("%s/%s", homeDir, DnoteDirName) - } else { - ret = dnoteDirEnv - } - - return ret -} - -func getHomeDir() (string, error) { - homeDirEnv := os.Getenv("DNOTE_HOME_DIR") - if homeDirEnv != "" { - return homeDirEnv, nil - } - - usr, err := user.Current() - if err != nil { - return "", errors.Wrap(err, "Failed to get current user") - } - - return usr.HomeDir, nil -} - -// InitDB initializes the database. -// Ideally this process must be a part of migration sequence. But it is performed -// seaprately because it is a prerequisite for legacy migration. -func InitDB(ctx DnoteCtx) error { - db := ctx.DB - - _, err := db.Exec(`CREATE TABLE IF NOT EXISTS notes - ( - id integer PRIMARY KEY AUTOINCREMENT, - uuid text NOT NULL, - book_uuid text NOT NULL, - content text NOT NULL, - added_on integer NOT NULL, - edited_on integer DEFAULT 0, - public bool DEFAULT false - )`) - if err != nil { - return errors.Wrap(err, "creating notes table") - } - - _, err = db.Exec(`CREATE TABLE IF NOT EXISTS books - ( - uuid text PRIMARY KEY, - label text NOT NULL - )`) - if err != nil { - return errors.Wrap(err, "creating books table") - } - - _, err = db.Exec(`CREATE TABLE IF NOT EXISTS system - ( - key string NOT NULL, - value text NOT NULL - )`) - if err != nil { - return errors.Wrap(err, "creating system table") - } - - _, err = db.Exec(`CREATE TABLE IF NOT EXISTS actions - ( - uuid text PRIMARY KEY, - schema integer NOT NULL, - type text NOT NULL, - data text NOT NULL, - timestamp integer NOT NULL - )`) - if err != nil { - return errors.Wrap(err, "creating actions table") - } - - _, err = db.Exec(` - CREATE UNIQUE INDEX IF NOT EXISTS idx_books_label ON books(label); - CREATE UNIQUE INDEX IF NOT EXISTS idx_notes_uuid ON notes(uuid); - CREATE UNIQUE INDEX IF NOT EXISTS idx_books_uuid ON books(uuid); - CREATE INDEX IF NOT EXISTS idx_notes_book_uuid ON notes(book_uuid);`) - if err != nil { - return errors.Wrap(err, "creating indices") - } - - return nil -} diff --git a/cli/main.go b/cli/main.go deleted file mode 100644 index 212345d1..00000000 --- a/cli/main.go +++ /dev/null @@ -1,80 +0,0 @@ -/* Copyright (C) 2019 Monomax Software Pty Ltd - * - * This file is part of Dnote CLI. - * - * Dnote CLI 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 CLI 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 CLI. If not, see . - */ - -package main - -import ( - "os" - - "github.com/dnote/dnote/cli/cmd/root" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" - _ "github.com/mattn/go-sqlite3" - "github.com/pkg/errors" - - // commands - "github.com/dnote/dnote/cli/cmd/add" - "github.com/dnote/dnote/cli/cmd/cat" - "github.com/dnote/dnote/cli/cmd/edit" - "github.com/dnote/dnote/cli/cmd/find" - "github.com/dnote/dnote/cli/cmd/login" - "github.com/dnote/dnote/cli/cmd/logout" - "github.com/dnote/dnote/cli/cmd/ls" - "github.com/dnote/dnote/cli/cmd/remove" - "github.com/dnote/dnote/cli/cmd/sync" - "github.com/dnote/dnote/cli/cmd/version" - "github.com/dnote/dnote/cli/cmd/view" -) - -// apiEndpoint and versionTag are populated during link time -var apiEndpoint string -var versionTag = "master" - -func main() { - ctx, err := infra.NewCtx(apiEndpoint, versionTag) - if err != nil { - panic(errors.Wrap(err, "initializing context")) - } - defer ctx.DB.Close() - - if err := root.Prepare(ctx); err != nil { - panic(errors.Wrap(err, "preparing dnote run")) - } - - ctx, err = infra.SetupCtx(ctx) - if err != nil { - panic(errors.Wrap(err, "setting up context")) - } - - root.Register(remove.NewCmd(ctx)) - root.Register(edit.NewCmd(ctx)) - root.Register(login.NewCmd(ctx)) - root.Register(logout.NewCmd(ctx)) - root.Register(add.NewCmd(ctx)) - root.Register(ls.NewCmd(ctx)) - root.Register(sync.NewCmd(ctx)) - root.Register(version.NewCmd(ctx)) - root.Register(cat.NewCmd(ctx)) - root.Register(view.NewCmd(ctx)) - root.Register(find.NewCmd(ctx)) - - if err := root.Execute(); err != nil { - log.Errorf("%s\n", err.Error()) - os.Exit(1) - } -} diff --git a/cli/main_test.go b/cli/main_test.go deleted file mode 100644 index 8d0aeb1d..00000000 --- a/cli/main_test.go +++ /dev/null @@ -1,339 +0,0 @@ -/* Copyright (C) 2019 Monomax Software Pty Ltd - * - * This file is part of Dnote CLI. - * - * Dnote CLI 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 CLI 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 CLI. If not, see . - */ - -package main - -import ( - "fmt" - "log" - "os" - "os/exec" - "testing" - - "github.com/dnote/dnote/cli/core" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/testutils" - "github.com/dnote/dnote/cli/utils" - "github.com/pkg/errors" -) - -var binaryName = "test-dnote" - -func TestMain(m *testing.M) { - if err := exec.Command("go", "build", "--tags", "fts5", "-o", binaryName).Run(); err != nil { - log.Print(errors.Wrap(err, "building a binary").Error()) - os.Exit(1) - } - - os.Exit(m.Run()) -} - -func TestInit(t *testing.T) { - // Set up - ctx := testutils.InitEnv(t, "./tmp", "./testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - // Execute - testutils.RunDnoteCmd(t, ctx, binaryName) - - // Test - if !utils.FileExists(ctx.DnoteDir) { - t.Errorf("dnote directory was not initialized") - } - if !utils.FileExists(fmt.Sprintf("%s/%s", ctx.DnoteDir, core.ConfigFilename)) { - t.Errorf("config file was not initialized") - } - - db := ctx.DB - - var notesTableCount, booksTableCount, systemTableCount int - testutils.MustScan(t, "counting notes", - db.QueryRow("SELECT count(*) FROM sqlite_master WHERE type = ? AND name = ?", "table", "notes"), ¬esTableCount) - testutils.MustScan(t, "counting books", - db.QueryRow("SELECT count(*) FROM sqlite_master WHERE type = ? AND name = ?", "table", "books"), &booksTableCount) - testutils.MustScan(t, "counting system", - db.QueryRow("SELECT count(*) FROM sqlite_master WHERE type = ? AND name = ?", "table", "system"), &systemTableCount) - - testutils.AssertEqual(t, notesTableCount, 1, "notes table count mismatch") - testutils.AssertEqual(t, booksTableCount, 1, "books table count mismatch") - testutils.AssertEqual(t, systemTableCount, 1, "system table count mismatch") - - // test that all default system configurations are generated - var lastUpgrade, lastMaxUSN, lastSyncAt string - testutils.MustScan(t, "scanning last upgrade", - db.QueryRow("SELECT value FROM system WHERE key = ?", infra.SystemLastUpgrade), &lastUpgrade) - testutils.MustScan(t, "scanning last max usn", - db.QueryRow("SELECT value FROM system WHERE key = ?", infra.SystemLastMaxUSN), &lastMaxUSN) - testutils.MustScan(t, "scanning last sync at", - db.QueryRow("SELECT value FROM system WHERE key = ?", infra.SystemLastSyncAt), &lastSyncAt) - - testutils.AssertNotEqual(t, lastUpgrade, "", "last upgrade should not be empty") - testutils.AssertNotEqual(t, lastMaxUSN, "", "last max usn should not be empty") - testutils.AssertNotEqual(t, lastSyncAt, "", "last sync at should not be empty") -} - -func TestAddNote_NewBook_BodyFlag(t *testing.T) { - // Set up - ctx := testutils.InitEnv(t, "./tmp", "./testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - // Execute - testutils.RunDnoteCmd(t, ctx, binaryName, "add", "js", "-c", "foo") - - // Test - db := ctx.DB - - var noteCount, bookCount int - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - - testutils.AssertEqualf(t, bookCount, 1, "book count mismatch") - testutils.AssertEqualf(t, noteCount, 1, "note count mismatch") - - var book core.Book - testutils.MustScan(t, "getting book", db.QueryRow("SELECT uuid, dirty FROM books where label = ?", "js"), &book.UUID, &book.Dirty) - var note core.Note - testutils.MustScan(t, "getting note", - db.QueryRow("SELECT uuid, body, added_on, dirty FROM notes where book_uuid = ?", book.UUID), ¬e.UUID, ¬e.Body, ¬e.AddedOn, ¬e.Dirty) - - testutils.AssertEqual(t, book.Dirty, true, "Book dirty mismatch") - - testutils.AssertNotEqual(t, note.UUID, "", "Note should have UUID") - testutils.AssertEqual(t, note.Body, "foo", "Note body mismatch") - testutils.AssertEqual(t, note.Dirty, true, "Note dirty mismatch") - testutils.AssertNotEqual(t, note.AddedOn, int64(0), "Note added_on mismatch") -} - -func TestAddNote_ExistingBook_BodyFlag(t *testing.T) { - // Set up - ctx := testutils.InitEnv(t, "./tmp", "./testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - testutils.Setup3(t, ctx) - - // Execute - testutils.RunDnoteCmd(t, ctx, binaryName, "add", "js", "-c", "foo") - - // Test - db := ctx.DB - - var noteCount, bookCount int - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - - testutils.AssertEqualf(t, bookCount, 1, "book count mismatch") - testutils.AssertEqualf(t, noteCount, 2, "note count mismatch") - - var n1, n2 core.Note - testutils.MustScan(t, "getting n1", - db.QueryRow("SELECT uuid, body, added_on, dirty FROM notes WHERE book_uuid = ? AND uuid = ?", "js-book-uuid", "43827b9a-c2b0-4c06-a290-97991c896653"), &n1.UUID, &n1.Body, &n1.AddedOn, &n1.Dirty) - testutils.MustScan(t, "getting n2", - db.QueryRow("SELECT uuid, body, added_on, dirty FROM notes WHERE book_uuid = ? AND body = ?", "js-book-uuid", "foo"), &n2.UUID, &n2.Body, &n2.AddedOn, &n2.Dirty) - - var book core.Book - testutils.MustScan(t, "getting book", db.QueryRow("SELECT dirty FROM books where label = ?", "js"), &book.Dirty) - - testutils.AssertEqual(t, book.Dirty, false, "Book dirty mismatch") - - testutils.AssertNotEqual(t, n1.UUID, "", "n1 should have UUID") - testutils.AssertEqual(t, n1.Body, "Booleans have toString()", "n1 body mismatch") - testutils.AssertEqual(t, n1.AddedOn, int64(1515199943), "n1 added_on mismatch") - testutils.AssertEqual(t, n1.Dirty, false, "n1 dirty mismatch") - - testutils.AssertNotEqual(t, n2.UUID, "", "n2 should have UUID") - testutils.AssertEqual(t, n2.Body, "foo", "n2 body mismatch") - testutils.AssertEqual(t, n2.Dirty, true, "n2 dirty mismatch") -} - -func TestEditNote_BodyFlag(t *testing.T) { - // Set up - ctx := testutils.InitEnv(t, "./tmp", "./testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - testutils.Setup4(t, ctx) - - // Execute - testutils.RunDnoteCmd(t, ctx, binaryName, "edit", "2", "-c", "foo bar") - - // Test - db := ctx.DB - - var noteCount, bookCount int - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - - testutils.AssertEqualf(t, bookCount, 1, "book count mismatch") - testutils.AssertEqualf(t, noteCount, 2, "note count mismatch") - - var n1, n2 core.Note - testutils.MustScan(t, "getting n1", - db.QueryRow("SELECT uuid, body, added_on, dirty FROM notes where book_uuid = ? AND uuid = ?", "js-book-uuid", "43827b9a-c2b0-4c06-a290-97991c896653"), &n1.UUID, &n1.Body, &n1.AddedOn, &n1.Dirty) - testutils.MustScan(t, "getting n2", - db.QueryRow("SELECT uuid, body, added_on, dirty FROM notes where book_uuid = ? AND uuid = ?", "js-book-uuid", "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f"), &n2.UUID, &n2.Body, &n2.AddedOn, &n2.Dirty) - - testutils.AssertEqual(t, n1.UUID, "43827b9a-c2b0-4c06-a290-97991c896653", "n1 should have UUID") - testutils.AssertEqual(t, n1.Body, "Booleans have toString()", "n1 body mismatch") - testutils.AssertEqual(t, n1.Dirty, false, "n1 dirty mismatch") - - testutils.AssertEqual(t, n2.UUID, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "Note should have UUID") - testutils.AssertEqual(t, n2.Body, "foo bar", "Note body mismatch") - testutils.AssertEqual(t, n2.Dirty, true, "n2 dirty mismatch") - testutils.AssertNotEqual(t, n2.EditedOn, 0, "Note edited_on mismatch") -} - -func TestRemoveNote(t *testing.T) { - // Set up - ctx := testutils.InitEnv(t, "./tmp", "./testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - testutils.Setup2(t, ctx) - - // Execute - testutils.WaitDnoteCmd(t, ctx, testutils.UserConfirm, binaryName, "remove", "js", "1") - - // Test - db := ctx.DB - - var noteCount, bookCount, jsNoteCount, linuxNoteCount int - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting js notes", db.QueryRow("SELECT count(*) FROM notes WHERE book_uuid = ?", "js-book-uuid"), &jsNoteCount) - testutils.MustScan(t, "counting linux notes", db.QueryRow("SELECT count(*) FROM notes WHERE book_uuid = ?", "linux-book-uuid"), &linuxNoteCount) - - testutils.AssertEqualf(t, bookCount, 2, "book count mismatch") - testutils.AssertEqualf(t, noteCount, 3, "note count mismatch") - testutils.AssertEqual(t, jsNoteCount, 2, "js book should have 2 notes") - testutils.AssertEqual(t, linuxNoteCount, 1, "linux book book should have 1 note") - - var b1, b2 core.Book - var n1, n2, n3 core.Note - testutils.MustScan(t, "getting b1", - db.QueryRow("SELECT label, deleted, usn FROM books WHERE uuid = ?", "js-book-uuid"), - &b1.Label, &b1.Deleted, &b1.USN) - testutils.MustScan(t, "getting b2", - db.QueryRow("SELECT label, deleted, usn FROM books WHERE uuid = ?", "linux-book-uuid"), - &b2.Label, &b2.Deleted, &b2.USN) - testutils.MustScan(t, "getting n1", - db.QueryRow("SELECT uuid, body, added_on, deleted, dirty, usn FROM notes WHERE book_uuid = ? AND uuid = ?", "js-book-uuid", "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f"), - &n1.UUID, &n1.Body, &n1.AddedOn, &n1.Deleted, &n1.Dirty, &n1.USN) - testutils.MustScan(t, "getting n2", - db.QueryRow("SELECT uuid, body, added_on, deleted, dirty, usn FROM notes WHERE book_uuid = ? AND uuid = ?", "js-book-uuid", "43827b9a-c2b0-4c06-a290-97991c896653"), - &n2.UUID, &n2.Body, &n2.AddedOn, &n2.Deleted, &n2.Dirty, &n2.USN) - testutils.MustScan(t, "getting n3", - db.QueryRow("SELECT uuid, body, added_on, deleted, dirty, usn FROM notes WHERE book_uuid = ? AND uuid = ?", "linux-book-uuid", "3e065d55-6d47-42f2-a6bf-f5844130b2d2"), - &n3.UUID, &n3.Body, &n3.AddedOn, &n3.Deleted, &n3.Dirty, &n3.USN) - - testutils.AssertEqual(t, b1.Label, "js", "b1 label mismatch") - testutils.AssertEqual(t, b1.Deleted, false, "b1 deleted mismatch") - testutils.AssertEqual(t, b1.Dirty, false, "b1 Dirty mismatch") - testutils.AssertEqual(t, b1.USN, 111, "b1 usn mismatch") - - testutils.AssertEqual(t, b2.Label, "linux", "b2 label mismatch") - testutils.AssertEqual(t, b2.Deleted, false, "b2 deleted mismatch") - testutils.AssertEqual(t, b2.Dirty, false, "b2 Dirty mismatch") - testutils.AssertEqual(t, b2.USN, 122, "b2 usn mismatch") - - testutils.AssertEqual(t, n1.UUID, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "n1 should have UUID") - testutils.AssertEqual(t, n1.Body, "", "n1 body mismatch") - testutils.AssertEqual(t, n1.Deleted, true, "n1 deleted mismatch") - testutils.AssertEqual(t, n1.Dirty, true, "n1 Dirty mismatch") - testutils.AssertEqual(t, n1.USN, 11, "n1 usn mismatch") - - testutils.AssertEqual(t, n2.UUID, "43827b9a-c2b0-4c06-a290-97991c896653", "n2 should have UUID") - testutils.AssertEqual(t, n2.Body, "n2 body", "n2 body mismatch") - testutils.AssertEqual(t, n2.Deleted, false, "n2 deleted mismatch") - testutils.AssertEqual(t, n2.Dirty, false, "n2 Dirty mismatch") - testutils.AssertEqual(t, n2.USN, 12, "n2 usn mismatch") - - testutils.AssertEqual(t, n3.UUID, "3e065d55-6d47-42f2-a6bf-f5844130b2d2", "n3 should have UUID") - testutils.AssertEqual(t, n3.Body, "n3 body", "n3 body mismatch") - testutils.AssertEqual(t, n3.Deleted, false, "n3 deleted mismatch") - testutils.AssertEqual(t, n3.Dirty, false, "n3 Dirty mismatch") - testutils.AssertEqual(t, n3.USN, 13, "n3 usn mismatch") -} - -func TestRemoveBook(t *testing.T) { - // Set up - ctx := testutils.InitEnv(t, "./tmp", "./testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - testutils.Setup2(t, ctx) - - // Execute - testutils.WaitDnoteCmd(t, ctx, testutils.UserConfirm, binaryName, "remove", "-b", "js") - - // Test - db := ctx.DB - - var noteCount, bookCount, jsNoteCount, linuxNoteCount int - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting js notes", db.QueryRow("SELECT count(*) FROM notes WHERE book_uuid = ?", "js-book-uuid"), &jsNoteCount) - testutils.MustScan(t, "counting linux notes", db.QueryRow("SELECT count(*) FROM notes WHERE book_uuid = ?", "linux-book-uuid"), &linuxNoteCount) - - testutils.AssertEqualf(t, bookCount, 2, "book count mismatch") - testutils.AssertEqualf(t, noteCount, 3, "note count mismatch") - testutils.AssertEqual(t, jsNoteCount, 2, "js book should have 2 notes") - testutils.AssertEqual(t, linuxNoteCount, 1, "linux book book should have 1 note") - - var b1, b2 core.Book - var n1, n2, n3 core.Note - testutils.MustScan(t, "getting b1", - db.QueryRow("SELECT label, dirty, deleted, usn FROM books WHERE uuid = ?", "js-book-uuid"), - &b1.Label, &b1.Dirty, &b1.Deleted, &b1.USN) - testutils.MustScan(t, "getting b2", - db.QueryRow("SELECT label, dirty, deleted, usn FROM books WHERE uuid = ?", "linux-book-uuid"), - &b2.Label, &b2.Dirty, &b2.Deleted, &b2.USN) - testutils.MustScan(t, "getting n1", - db.QueryRow("SELECT uuid, body, added_on, dirty, deleted, usn FROM notes WHERE book_uuid = ? AND uuid = ?", "js-book-uuid", "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f"), - &n1.UUID, &n1.Body, &n1.AddedOn, &n1.Deleted, &n1.Dirty, &n1.USN) - testutils.MustScan(t, "getting n2", - db.QueryRow("SELECT uuid, body, added_on, dirty, deleted, usn FROM notes WHERE book_uuid = ? AND uuid = ?", "js-book-uuid", "43827b9a-c2b0-4c06-a290-97991c896653"), - &n2.UUID, &n2.Body, &n2.AddedOn, &n2.Deleted, &n2.Dirty, &n2.USN) - testutils.MustScan(t, "getting n3", - db.QueryRow("SELECT uuid, body, added_on, dirty, deleted, usn FROM notes WHERE book_uuid = ? AND uuid = ?", "linux-book-uuid", "3e065d55-6d47-42f2-a6bf-f5844130b2d2"), - &n3.UUID, &n3.Body, &n3.AddedOn, &n3.Deleted, &n3.Dirty, &n3.USN) - - testutils.AssertNotEqual(t, b1.Label, "js", "b1 label mismatch") - testutils.AssertEqual(t, b1.Dirty, true, "b1 Dirty mismatch") - testutils.AssertEqual(t, b1.Deleted, true, "b1 deleted mismatch") - testutils.AssertEqual(t, b1.USN, 111, "b1 usn mismatch") - - testutils.AssertEqual(t, b2.Label, "linux", "b2 label mismatch") - testutils.AssertEqual(t, b2.Dirty, false, "b2 Dirty mismatch") - testutils.AssertEqual(t, b2.Deleted, false, "b2 deleted mismatch") - testutils.AssertEqual(t, b2.USN, 122, "b2 usn mismatch") - - testutils.AssertEqual(t, n1.UUID, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "n1 should have UUID") - testutils.AssertEqual(t, n1.Body, "", "n1 body mismatch") - testutils.AssertEqual(t, n1.Dirty, true, "n1 Dirty mismatch") - testutils.AssertEqual(t, n1.Deleted, true, "n1 deleted mismatch") - testutils.AssertEqual(t, n1.USN, 11, "n1 usn mismatch") - - testutils.AssertEqual(t, n2.UUID, "43827b9a-c2b0-4c06-a290-97991c896653", "n2 should have UUID") - testutils.AssertEqual(t, n2.Body, "", "n2 body mismatch") - testutils.AssertEqual(t, n2.Dirty, true, "n2 Dirty mismatch") - testutils.AssertEqual(t, n2.Deleted, true, "n2 deleted mismatch") - testutils.AssertEqual(t, n2.USN, 12, "n2 usn mismatch") - - testutils.AssertEqual(t, n3.UUID, "3e065d55-6d47-42f2-a6bf-f5844130b2d2", "n3 should have UUID") - testutils.AssertEqual(t, n3.Body, "n3 body", "n3 body mismatch") - testutils.AssertEqual(t, n3.Dirty, false, "n3 Dirty mismatch") - testutils.AssertEqual(t, n3.Deleted, false, "n3 deleted mismatch") - testutils.AssertEqual(t, n3.USN, 13, "n3 usn mismatch") -} diff --git a/cli/migrate/migrate_test.go b/cli/migrate/migrate_test.go deleted file mode 100644 index a8e0122e..00000000 --- a/cli/migrate/migrate_test.go +++ /dev/null @@ -1,1115 +0,0 @@ -/* Copyright (C) 2019 Monomax Software Pty Ltd - * - * This file is part of Dnote CLI. - * - * Dnote CLI 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 CLI 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 CLI. If not, see . - */ - -package migrate - -import ( - "encoding/json" - "fmt" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/dnote/actions" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/testutils" - "github.com/dnote/dnote/cli/utils" - "github.com/pkg/errors" -) - -func TestExecute_bump_schema(t *testing.T) { - testCases := []struct { - schemaKey string - }{ - { - schemaKey: infra.SystemSchema, - }, - { - schemaKey: infra.SystemRemoteSchema, - }, - } - - for _, tc := range testCases { - func() { - // set up - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - testutils.MustExec(t, "inserting a schema", db, "INSERT INTO system (key, value) VALUES (?, ?)", tc.schemaKey, 8) - - m1 := migration{ - name: "noop", - run: func(ctx infra.DnoteCtx, db *infra.DB) error { - return nil - }, - } - m2 := migration{ - name: "noop", - run: func(ctx infra.DnoteCtx, db *infra.DB) error { - return nil - }, - } - - // execute - err := execute(ctx, m1, tc.schemaKey) - if err != nil { - t.Fatal(errors.Wrap(err, "failed to execute")) - } - err = execute(ctx, m2, tc.schemaKey) - if err != nil { - t.Fatal(errors.Wrap(err, "failed to execute")) - } - - // test - var schema int - testutils.MustScan(t, "getting schema", db.QueryRow("SELECT value FROM system WHERE key = ?", tc.schemaKey), &schema) - testutils.AssertEqual(t, schema, 10, "schema was not incremented properly") - }() - } -} - -func TestRun_nonfresh(t *testing.T) { - testCases := []struct { - mode int - schemaKey string - }{ - { - mode: LocalMode, - schemaKey: infra.SystemSchema, - }, - { - mode: RemoteMode, - schemaKey: infra.SystemRemoteSchema, - }, - } - - for _, tc := range testCases { - func() { - // set up - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - testutils.MustExec(t, "inserting a schema", db, "INSERT INTO system (key, value) VALUES (?, ?)", tc.schemaKey, 2) - testutils.MustExec(t, "creating a temporary table for testing", db, - "CREATE TABLE migrate_run_test ( name string )") - - sequence := []migration{ - migration{ - name: "v1", - run: func(ctx infra.DnoteCtx, db *infra.DB) error { - testutils.MustExec(t, "marking v1 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v1") - return nil - }, - }, - migration{ - name: "v2", - run: func(ctx infra.DnoteCtx, db *infra.DB) error { - testutils.MustExec(t, "marking v2 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v2") - return nil - }, - }, - migration{ - name: "v3", - run: func(ctx infra.DnoteCtx, db *infra.DB) error { - testutils.MustExec(t, "marking v3 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v3") - return nil - }, - }, - migration{ - name: "v4", - run: func(ctx infra.DnoteCtx, db *infra.DB) error { - testutils.MustExec(t, "marking v4 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v4") - return nil - }, - }, - } - - // execute - err := Run(ctx, sequence, tc.mode) - if err != nil { - t.Fatal(errors.Wrap(err, "failed to run")) - } - - // test - var schema int - testutils.MustScan(t, fmt.Sprintf("getting schema for %s", tc.schemaKey), db.QueryRow("SELECT value FROM system WHERE key = ?", tc.schemaKey), &schema) - testutils.AssertEqual(t, schema, 4, fmt.Sprintf("schema was not updated for %s", tc.schemaKey)) - - var testRunCount int - testutils.MustScan(t, "counting test runs", db.QueryRow("SELECT count(*) FROM migrate_run_test"), &testRunCount) - testutils.AssertEqual(t, testRunCount, 2, "test run count mismatch") - - var testRun1, testRun2 string - testutils.MustScan(t, "finding test run 1", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v3"), &testRun1) - testutils.MustScan(t, "finding test run 2", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v4"), &testRun2) - }() - } - -} - -func TestRun_fresh(t *testing.T) { - testCases := []struct { - mode int - schemaKey string - }{ - { - mode: LocalMode, - schemaKey: infra.SystemSchema, - }, - { - mode: RemoteMode, - schemaKey: infra.SystemRemoteSchema, - }, - } - - for _, tc := range testCases { - func() { - // set up - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - testutils.MustExec(t, "creating a temporary table for testing", db, - "CREATE TABLE migrate_run_test ( name string )") - - sequence := []migration{ - migration{ - name: "v1", - run: func(ctx infra.DnoteCtx, db *infra.DB) error { - testutils.MustExec(t, "marking v1 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v1") - return nil - }, - }, - migration{ - name: "v2", - run: func(ctx infra.DnoteCtx, db *infra.DB) error { - testutils.MustExec(t, "marking v2 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v2") - return nil - }, - }, - migration{ - name: "v3", - run: func(ctx infra.DnoteCtx, db *infra.DB) error { - testutils.MustExec(t, "marking v3 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v3") - return nil - }, - }, - } - - // execute - err := Run(ctx, sequence, tc.mode) - if err != nil { - t.Fatal(errors.Wrap(err, "failed to run")) - } - - // test - var schema int - testutils.MustScan(t, "getting schema", db.QueryRow("SELECT value FROM system WHERE key = ?", tc.schemaKey), &schema) - testutils.AssertEqual(t, schema, 3, "schema was not updated") - - var testRunCount int - testutils.MustScan(t, "counting test runs", db.QueryRow("SELECT count(*) FROM migrate_run_test"), &testRunCount) - testutils.AssertEqual(t, testRunCount, 3, "test run count mismatch") - - var testRun1, testRun2, testRun3 string - testutils.MustScan(t, "finding test run 1", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v1"), &testRun1) - testutils.MustScan(t, "finding test run 2", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v2"), &testRun2) - testutils.MustScan(t, "finding test run 2", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v3"), &testRun3) - }() - } -} - -func TestRun_up_to_date(t *testing.T) { - testCases := []struct { - mode int - schemaKey string - }{ - { - mode: LocalMode, - schemaKey: infra.SystemSchema, - }, - { - mode: RemoteMode, - schemaKey: infra.SystemRemoteSchema, - }, - } - - for _, tc := range testCases { - func() { - // set up - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - testutils.MustExec(t, "creating a temporary table for testing", db, - "CREATE TABLE migrate_run_test ( name string )") - - testutils.MustExec(t, "inserting a schema", db, "INSERT INTO system (key, value) VALUES (?, ?)", tc.schemaKey, 3) - - sequence := []migration{ - migration{ - name: "v1", - run: func(ctx infra.DnoteCtx, db *infra.DB) error { - testutils.MustExec(t, "marking v1 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v1") - return nil - }, - }, - migration{ - name: "v2", - run: func(ctx infra.DnoteCtx, db *infra.DB) error { - testutils.MustExec(t, "marking v2 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v2") - return nil - }, - }, - migration{ - name: "v3", - run: func(ctx infra.DnoteCtx, db *infra.DB) error { - testutils.MustExec(t, "marking v3 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v3") - return nil - }, - }, - } - - // execute - err := Run(ctx, sequence, tc.mode) - if err != nil { - t.Fatal(errors.Wrap(err, "failed to run")) - } - - // test - var schema int - testutils.MustScan(t, "getting schema", db.QueryRow("SELECT value FROM system WHERE key = ?", tc.schemaKey), &schema) - testutils.AssertEqual(t, schema, 3, "schema was not updated") - - var testRunCount int - testutils.MustScan(t, "counting test runs", db.QueryRow("SELECT count(*) FROM migrate_run_test"), &testRunCount) - testutils.AssertEqual(t, testRunCount, 0, "test run count mismatch") - }() - } -} - -func TestLocalMigration1(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/local-1-pre-schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - - data := testutils.MustMarshalJSON(t, actions.AddBookDataV1{BookName: "js"}) - a1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting action", db, - "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a1UUID, 1, "add_book", string(data), 1537829463) - - data = testutils.MustMarshalJSON(t, actions.EditNoteDataV1{NoteUUID: "note-1-uuid", FromBook: "js", ToBook: "", Content: "note 1"}) - a2UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting action", db, - "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a2UUID, 1, "edit_note", string(data), 1537829463) - - data = testutils.MustMarshalJSON(t, actions.EditNoteDataV1{NoteUUID: "note-2-uuid", FromBook: "js", ToBook: "", Content: "note 2"}) - a3UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting action", db, - "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a3UUID, 1, "edit_note", string(data), 1537829463) - - // Execute - tx, err := db.Begin() - if err != nil { - t.Fatal(errors.Wrap(err, "beginning a transaction")) - } - - err = lm1.run(ctx, tx) - if err != nil { - tx.Rollback() - t.Fatal(errors.Wrap(err, "failed to run")) - } - - tx.Commit() - - // Test - var actionCount int - testutils.MustScan(t, "counting actions", db.QueryRow("SELECT count(*) FROM actions"), &actionCount) - testutils.AssertEqual(t, actionCount, 3, "action count mismatch") - - var a1, a2, a3 actions.Action - testutils.MustScan(t, "getting action 1", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a1UUID), - &a1.Schema, &a1.Type, &a1.Data, &a1.Timestamp) - testutils.MustScan(t, "getting action 2", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a2UUID), - &a2.Schema, &a2.Type, &a2.Data, &a2.Timestamp) - testutils.MustScan(t, "getting action 3", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a3UUID), - &a3.Schema, &a3.Type, &a3.Data, &a3.Timestamp) - - var a1Data actions.AddBookDataV1 - var a2Data, a3Data actions.EditNoteDataV3 - testutils.MustUnmarshalJSON(t, a1.Data, &a1Data) - testutils.MustUnmarshalJSON(t, a2.Data, &a2Data) - testutils.MustUnmarshalJSON(t, a3.Data, &a3Data) - - testutils.AssertEqual(t, a1.Schema, 1, "a1 schema mismatch") - testutils.AssertEqual(t, a1.Type, "add_book", "a1 type mismatch") - testutils.AssertEqual(t, a1.Timestamp, int64(1537829463), "a1 timestamp mismatch") - testutils.AssertEqual(t, a1Data.BookName, "js", "a1 data book_name mismatch") - - testutils.AssertEqual(t, a2.Schema, 3, "a2 schema mismatch") - testutils.AssertEqual(t, a2.Type, "edit_note", "a2 type mismatch") - testutils.AssertEqual(t, a2.Timestamp, int64(1537829463), "a2 timestamp mismatch") - testutils.AssertEqual(t, a2Data.NoteUUID, "note-1-uuid", "a2 data note_uuid mismatch") - testutils.AssertEqual(t, a2Data.BookName, (*string)(nil), "a2 data book_name mismatch") - testutils.AssertEqual(t, *a2Data.Content, "note 1", "a2 data content mismatch") - testutils.AssertEqual(t, *a2Data.Public, false, "a2 data public mismatch") - - testutils.AssertEqual(t, a3.Schema, 3, "a3 schema mismatch") - testutils.AssertEqual(t, a3.Type, "edit_note", "a3 type mismatch") - testutils.AssertEqual(t, a3.Timestamp, int64(1537829463), "a3 timestamp mismatch") - testutils.AssertEqual(t, a3Data.NoteUUID, "note-2-uuid", "a3 data note_uuid mismatch") - testutils.AssertEqual(t, a3Data.BookName, (*string)(nil), "a3 data book_name mismatch") - testutils.AssertEqual(t, *a3Data.Content, "note 2", "a3 data content mismatch") - testutils.AssertEqual(t, *a3Data.Public, false, "a3 data public mismatch") -} - -func TestLocalMigration2(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/local-1-pre-schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - - c1 := "note 1 - v1" - c2 := "note 1 - v2" - css := "css" - - b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting css book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "css") - - data := testutils.MustMarshalJSON(t, actions.AddNoteDataV2{NoteUUID: "note-1-uuid", BookName: "js", Content: "note 1", Public: false}) - a1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting action", db, - "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a1UUID, 2, "add_note", string(data), 1537829463) - - data = testutils.MustMarshalJSON(t, actions.EditNoteDataV2{NoteUUID: "note-1-uuid", FromBook: "js", ToBook: nil, Content: &c1, Public: nil}) - a2UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting action", db, - "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a2UUID, 2, "edit_note", string(data), 1537829463) - - data = testutils.MustMarshalJSON(t, actions.EditNoteDataV2{NoteUUID: "note-1-uuid", FromBook: "js", ToBook: &css, Content: &c2, Public: nil}) - a3UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting action", db, - "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a3UUID, 2, "edit_note", string(data), 1537829463) - - // Execute - tx, err := db.Begin() - if err != nil { - t.Fatal(errors.Wrap(err, "beginning a transaction")) - } - - err = lm2.run(ctx, tx) - if err != nil { - tx.Rollback() - t.Fatal(errors.Wrap(err, "failed to run")) - } - - tx.Commit() - - // Test - var actionCount int - testutils.MustScan(t, "counting actions", db.QueryRow("SELECT count(*) FROM actions"), &actionCount) - testutils.AssertEqual(t, actionCount, 3, "action count mismatch") - - var a1, a2, a3 actions.Action - testutils.MustScan(t, "getting action 1", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a1UUID), - &a1.Schema, &a1.Type, &a1.Data, &a1.Timestamp) - testutils.MustScan(t, "getting action 2", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a2UUID), - &a2.Schema, &a2.Type, &a2.Data, &a2.Timestamp) - testutils.MustScan(t, "getting action 3", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a3UUID), - &a3.Schema, &a3.Type, &a3.Data, &a3.Timestamp) - - var a1Data actions.AddNoteDataV2 - var a2Data, a3Data actions.EditNoteDataV3 - testutils.MustUnmarshalJSON(t, a1.Data, &a1Data) - testutils.MustUnmarshalJSON(t, a2.Data, &a2Data) - testutils.MustUnmarshalJSON(t, a3.Data, &a3Data) - - testutils.AssertEqual(t, a1.Schema, 2, "a1 schema mismatch") - testutils.AssertEqual(t, a1.Type, "add_note", "a1 type mismatch") - testutils.AssertEqual(t, a1.Timestamp, int64(1537829463), "a1 timestamp mismatch") - testutils.AssertEqual(t, a1Data.NoteUUID, "note-1-uuid", "a1 data note_uuid mismatch") - testutils.AssertEqual(t, a1Data.BookName, "js", "a1 data book_name mismatch") - testutils.AssertEqual(t, a1Data.Public, false, "a1 data public mismatch") - - testutils.AssertEqual(t, a2.Schema, 3, "a2 schema mismatch") - testutils.AssertEqual(t, a2.Type, "edit_note", "a2 type mismatch") - testutils.AssertEqual(t, a2.Timestamp, int64(1537829463), "a2 timestamp mismatch") - testutils.AssertEqual(t, a2Data.NoteUUID, "note-1-uuid", "a2 data note_uuid mismatch") - testutils.AssertEqual(t, a2Data.BookName, (*string)(nil), "a2 data book_name mismatch") - testutils.AssertEqual(t, *a2Data.Content, c1, "a2 data content mismatch") - testutils.AssertEqual(t, a2Data.Public, (*bool)(nil), "a2 data public mismatch") - - testutils.AssertEqual(t, a3.Schema, 3, "a3 schema mismatch") - testutils.AssertEqual(t, a3.Type, "edit_note", "a3 type mismatch") - testutils.AssertEqual(t, a3.Timestamp, int64(1537829463), "a3 timestamp mismatch") - testutils.AssertEqual(t, a3Data.NoteUUID, "note-1-uuid", "a3 data note_uuid mismatch") - testutils.AssertEqual(t, *a3Data.BookName, "css", "a3 data book_name mismatch") - testutils.AssertEqual(t, *a3Data.Content, c2, "a3 data content mismatch") - testutils.AssertEqual(t, a3Data.Public, (*bool)(nil), "a3 data public mismatch") -} - -func TestLocalMigration3(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/local-1-pre-schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - - data := testutils.MustMarshalJSON(t, actions.AddNoteDataV2{NoteUUID: "note-1-uuid", BookName: "js", Content: "note 1", Public: false}) - a1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting action", db, - "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a1UUID, 2, "add_note", string(data), 1537829463) - - data = testutils.MustMarshalJSON(t, actions.RemoveNoteDataV1{NoteUUID: "note-1-uuid", BookName: "js"}) - a2UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting action", db, - "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a2UUID, 1, "remove_note", string(data), 1537829463) - - data = testutils.MustMarshalJSON(t, actions.RemoveNoteDataV1{NoteUUID: "note-2-uuid", BookName: "js"}) - a3UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting action", db, - "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a3UUID, 1, "remove_note", string(data), 1537829463) - - // Execute - tx, err := db.Begin() - if err != nil { - t.Fatal(errors.Wrap(err, "beginning a transaction")) - } - - err = lm3.run(ctx, tx) - if err != nil { - tx.Rollback() - t.Fatal(errors.Wrap(err, "failed to run")) - } - - tx.Commit() - - // Test - var actionCount int - testutils.MustScan(t, "counting actions", db.QueryRow("SELECT count(*) FROM actions"), &actionCount) - testutils.AssertEqual(t, actionCount, 3, "action count mismatch") - - var a1, a2, a3 actions.Action - testutils.MustScan(t, "getting action 1", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a1UUID), - &a1.Schema, &a1.Type, &a1.Data, &a1.Timestamp) - testutils.MustScan(t, "getting action 2", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a2UUID), - &a2.Schema, &a2.Type, &a2.Data, &a2.Timestamp) - testutils.MustScan(t, "getting action 3", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a3UUID), - &a3.Schema, &a3.Type, &a3.Data, &a3.Timestamp) - - var a1Data actions.AddNoteDataV2 - var a2Data, a3Data actions.RemoveNoteDataV2 - testutils.MustUnmarshalJSON(t, a1.Data, &a1Data) - testutils.MustUnmarshalJSON(t, a2.Data, &a2Data) - testutils.MustUnmarshalJSON(t, a3.Data, &a3Data) - - testutils.AssertEqual(t, a1.Schema, 2, "a1 schema mismatch") - testutils.AssertEqual(t, a1.Type, "add_note", "a1 type mismatch") - testutils.AssertEqual(t, a1.Timestamp, int64(1537829463), "a1 timestamp mismatch") - testutils.AssertEqual(t, a1Data.NoteUUID, "note-1-uuid", "a1 data note_uuid mismatch") - testutils.AssertEqual(t, a1Data.BookName, "js", "a1 data book_name mismatch") - testutils.AssertEqual(t, a1Data.Content, "note 1", "a1 data content mismatch") - testutils.AssertEqual(t, a1Data.Public, false, "a1 data public mismatch") - - testutils.AssertEqual(t, a2.Schema, 2, "a2 schema mismatch") - testutils.AssertEqual(t, a2.Type, "remove_note", "a2 type mismatch") - testutils.AssertEqual(t, a2.Timestamp, int64(1537829463), "a2 timestamp mismatch") - testutils.AssertEqual(t, a2Data.NoteUUID, "note-1-uuid", "a2 data note_uuid mismatch") - - testutils.AssertEqual(t, a3.Schema, 2, "a3 schema mismatch") - testutils.AssertEqual(t, a3.Type, "remove_note", "a3 type mismatch") - testutils.AssertEqual(t, a3.Timestamp, int64(1537829463), "a3 timestamp mismatch") - testutils.AssertEqual(t, a3Data.NoteUUID, "note-2-uuid", "a3 data note_uuid mismatch") -} - -func TestLocalMigration4(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/local-1-pre-schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - - b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting css book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "css") - n1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting css note", db, "INSERT INTO notes (uuid, book_uuid, content, added_on) VALUES (?, ?, ?, ?)", n1UUID, b1UUID, "n1 content", time.Now().UnixNano()) - - // Execute - tx, err := db.Begin() - if err != nil { - t.Fatal(errors.Wrap(err, "beginning a transaction")) - } - - err = lm4.run(ctx, tx) - if err != nil { - tx.Rollback() - t.Fatal(errors.Wrap(err, "failed to run")) - } - - tx.Commit() - - // Test - var n1Dirty, b1Dirty bool - var n1Deleted, b1Deleted bool - var n1USN, b1USN int - testutils.MustScan(t, "scanning the newly added dirty flag of n1", db.QueryRow("SELECT dirty, deleted, usn FROM notes WHERE uuid = ?", n1UUID), &n1Dirty, &n1Deleted, &n1USN) - testutils.MustScan(t, "scanning the newly added dirty flag of b1", db.QueryRow("SELECT dirty, deleted, usn FROM books WHERE uuid = ?", b1UUID), &b1Dirty, &b1Deleted, &b1USN) - - testutils.AssertEqual(t, n1Dirty, false, "n1 dirty flag should be false by default") - testutils.AssertEqual(t, b1Dirty, false, "b1 dirty flag should be false by default") - - testutils.AssertEqual(t, n1Deleted, false, "n1 deleted flag should be false by default") - testutils.AssertEqual(t, b1Deleted, false, "b1 deleted flag should be false by default") - - testutils.AssertEqual(t, n1USN, 0, "n1 usn flag should be 0 by default") - testutils.AssertEqual(t, b1USN, 0, "b1 usn flag should be 0 by default") -} - -func TestLocalMigration5(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/local-5-pre-schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - - b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting css book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "css") - b2UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting js book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "js") - - n1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting css note", db, "INSERT INTO notes (uuid, book_uuid, content, added_on) VALUES (?, ?, ?, ?)", n1UUID, b1UUID, "n1 content", time.Now().UnixNano()) - n2UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting css note", db, "INSERT INTO notes (uuid, book_uuid, content, added_on) VALUES (?, ?, ?, ?)", n2UUID, b1UUID, "n2 content", time.Now().UnixNano()) - n3UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting css note", db, "INSERT INTO notes (uuid, book_uuid, content, added_on) VALUES (?, ?, ?, ?)", n3UUID, b1UUID, "n3 content", time.Now().UnixNano()) - - data := testutils.MustMarshalJSON(t, actions.AddBookDataV1{BookName: "js"}) - testutils.MustExec(t, "inserting a1", db, - "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", "a1-uuid", 1, "add_book", string(data), 1537829463) - - data = testutils.MustMarshalJSON(t, actions.AddNoteDataV2{NoteUUID: n1UUID, BookName: "css", Content: "n1 content", Public: false}) - testutils.MustExec(t, "inserting a2", db, - "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", "a2-uuid", 1, "add_note", string(data), 1537829463) - - updatedContent := "updated content" - data = testutils.MustMarshalJSON(t, actions.EditNoteDataV3{NoteUUID: n2UUID, BookName: (*string)(nil), Content: &updatedContent, Public: (*bool)(nil)}) - testutils.MustExec(t, "inserting a3", db, - "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", "a3-uuid", 1, "edit_note", string(data), 1537829463) - - // Execute - tx, err := db.Begin() - if err != nil { - t.Fatal(errors.Wrap(err, "beginning a transaction")) - } - - err = lm5.run(ctx, tx) - if err != nil { - tx.Rollback() - t.Fatal(errors.Wrap(err, "failed to run")) - } - - tx.Commit() - - // Test - var b1Dirty, b2Dirty, n1Dirty, n2Dirty, n3Dirty bool - testutils.MustScan(t, "scanning the newly added dirty flag of b1", db.QueryRow("SELECT dirty FROM books WHERE uuid = ?", b1UUID), &b1Dirty) - testutils.MustScan(t, "scanning the newly added dirty flag of b2", db.QueryRow("SELECT dirty FROM books WHERE uuid = ?", b2UUID), &b2Dirty) - testutils.MustScan(t, "scanning the newly added dirty flag of n1", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", n1UUID), &n1Dirty) - testutils.MustScan(t, "scanning the newly added dirty flag of n2", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", n2UUID), &n2Dirty) - testutils.MustScan(t, "scanning the newly added dirty flag of n3", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", n3UUID), &n3Dirty) - - testutils.AssertEqual(t, b1Dirty, false, "b1 dirty flag should be false by default") - testutils.AssertEqual(t, b2Dirty, true, "b2 dirty flag should be false by default") - testutils.AssertEqual(t, n1Dirty, true, "n1 dirty flag should be false by default") - testutils.AssertEqual(t, n2Dirty, true, "n2 dirty flag should be false by default") - testutils.AssertEqual(t, n3Dirty, false, "n3 dirty flag should be false by default") -} - -func TestLocalMigration6(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/local-5-pre-schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - - data := testutils.MustMarshalJSON(t, actions.AddBookDataV1{BookName: "js"}) - a1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting action", db, - "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a1UUID, 1, "add_book", string(data), 1537829463) - - // Execute - tx, err := db.Begin() - if err != nil { - t.Fatal(errors.Wrap(err, "beginning a transaction")) - } - - err = lm5.run(ctx, tx) - if err != nil { - tx.Rollback() - t.Fatal(errors.Wrap(err, "failed to run")) - } - - tx.Commit() - - // Test - var count int - err = db.QueryRow("SELECT name FROM sqlite_master WHERE type='table' AND name = ?;", "actions").Scan(&count) - testutils.AssertEqual(t, count, 0, "actions table should have been deleted") -} - -func TestLocalMigration7_trash(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/local-7-pre-schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - - b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting trash book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "trash") - - // Execute - tx, err := db.Begin() - if err != nil { - t.Fatal(errors.Wrap(err, "beginning a transaction")) - } - - err = lm7.run(ctx, tx) - if err != nil { - tx.Rollback() - t.Fatal(errors.Wrap(err, "failed to run")) - } - - tx.Commit() - - // Test - var b1Label string - var b1Dirty bool - testutils.MustScan(t, "scanning b1 label", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty) - testutils.AssertEqual(t, b1Label, "trash (2)", "b1 label was not migrated") - testutils.AssertEqual(t, b1Dirty, true, "b1 was not marked dirty") -} - -func TestLocalMigration7_conflicts(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/local-7-pre-schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - - b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "conflicts") - - // Execute - tx, err := db.Begin() - if err != nil { - t.Fatal(errors.Wrap(err, "beginning a transaction")) - } - - err = lm7.run(ctx, tx) - if err != nil { - tx.Rollback() - t.Fatal(errors.Wrap(err, "failed to run")) - } - - tx.Commit() - - // Test - var b1Label string - var b1Dirty bool - testutils.MustScan(t, "scanning b1 label", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty) - testutils.AssertEqual(t, b1Label, "conflicts (2)", "b1 label was not migrated") - testutils.AssertEqual(t, b1Dirty, true, "b1 was not marked dirty") -} - -func TestLocalMigration7_conflicts_dup(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/local-7-pre-schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - - b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "conflicts") - b2UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "conflicts (2)") - - // Execute - tx, err := db.Begin() - if err != nil { - t.Fatal(errors.Wrap(err, "beginning a transaction")) - } - - err = lm7.run(ctx, tx) - if err != nil { - tx.Rollback() - t.Fatal(errors.Wrap(err, "failed to run")) - } - - tx.Commit() - - // Test - var b1Label, b2Label string - var b1Dirty, b2Dirty bool - testutils.MustScan(t, "scanning b1 label", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty) - testutils.MustScan(t, "scanning b2 label", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b2UUID), &b2Label, &b2Dirty) - testutils.AssertEqual(t, b1Label, "conflicts (3)", "b1 label was not migrated") - testutils.AssertEqual(t, b2Label, "conflicts (2)", "b1 label was not migrated") - testutils.AssertEqual(t, b1Dirty, true, "b1 was not marked dirty") - testutils.AssertEqual(t, b2Dirty, false, "b2 should not have been marked dirty") -} - -func TestLocalMigration8(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/local-8-pre-schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - - b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1") - - n1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting n1", db, `INSERT INTO notes - (id, uuid, book_uuid, content, added_on, edited_on, public, dirty, usn, deleted) VALUES - (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, 1, n1UUID, b1UUID, "n1 Body", 1, 2, true, true, 20, false) - n2UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting n2", db, `INSERT INTO notes - (id, uuid, book_uuid, content, added_on, edited_on, public, dirty, usn, deleted) VALUES - (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, 2, n2UUID, b1UUID, "", 3, 4, false, true, 21, true) - - // Execute - tx, err := db.Begin() - if err != nil { - t.Fatal(errors.Wrap(err, "beginning a transaction")) - } - - err = lm8.run(ctx, tx) - if err != nil { - tx.Rollback() - t.Fatal(errors.Wrap(err, "failed to run")) - } - - tx.Commit() - - // Test - var n1BookUUID, n1Body string - var n1AddedOn, n1EditedOn int64 - var n1USN int - var n1Public, n1Dirty, n1Deleted bool - testutils.MustScan(t, "scanning n1", db.QueryRow("SELECT book_uuid, body, added_on, edited_on, usn, public, dirty, deleted FROM notes WHERE uuid = ?", n1UUID), &n1BookUUID, &n1Body, &n1AddedOn, &n1EditedOn, &n1USN, &n1Public, &n1Dirty, &n1Deleted) - - var n2BookUUID, n2Body string - var n2AddedOn, n2EditedOn int64 - var n2USN int - var n2Public, n2Dirty, n2Deleted bool - testutils.MustScan(t, "scanning n2", db.QueryRow("SELECT book_uuid, body, added_on, edited_on, usn, public, dirty, deleted FROM notes WHERE uuid = ?", n2UUID), &n2BookUUID, &n2Body, &n2AddedOn, &n2EditedOn, &n2USN, &n2Public, &n2Dirty, &n2Deleted) - - testutils.AssertEqual(t, n1BookUUID, b1UUID, "n1 BookUUID mismatch") - testutils.AssertEqual(t, n1Body, "n1 Body", "n1 Body mismatch") - testutils.AssertEqual(t, n1AddedOn, int64(1), "n1 AddedOn mismatch") - testutils.AssertEqual(t, n1EditedOn, int64(2), "n1 EditedOn mismatch") - testutils.AssertEqual(t, n1USN, 20, "n1 USN mismatch") - testutils.AssertEqual(t, n1Public, true, "n1 Public mismatch") - testutils.AssertEqual(t, n1Dirty, true, "n1 Dirty mismatch") - testutils.AssertEqual(t, n1Deleted, false, "n1 Deleted mismatch") - - testutils.AssertEqual(t, n2BookUUID, b1UUID, "n2 BookUUID mismatch") - testutils.AssertEqual(t, n2Body, "", "n2 Body mismatch") - testutils.AssertEqual(t, n2AddedOn, int64(3), "n2 AddedOn mismatch") - testutils.AssertEqual(t, n2EditedOn, int64(4), "n2 EditedOn mismatch") - testutils.AssertEqual(t, n2USN, 21, "n2 USN mismatch") - testutils.AssertEqual(t, n2Public, false, "n2 Public mismatch") - testutils.AssertEqual(t, n2Dirty, true, "n2 Dirty mismatch") - testutils.AssertEqual(t, n2Deleted, true, "n2 Deleted mismatch") -} - -func TestLocalMigration9(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/local-9-pre-schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - - b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1") - - n1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting n1", db, `INSERT INTO notes - (uuid, book_uuid, body, added_on, edited_on, public, dirty, usn, deleted) VALUES - (?, ?, ?, ?, ?, ?, ?, ?, ?)`, n1UUID, b1UUID, "n1 Body", 1, 2, true, true, 20, false) - n2UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting n2", db, `INSERT INTO notes - (uuid, book_uuid, body, added_on, edited_on, public, dirty, usn, deleted) VALUES - (?, ?, ?, ?, ?, ?, ?, ?, ?)`, n2UUID, b1UUID, "n2 Body", 3, 4, false, true, 21, false) - - // Execute - tx, err := db.Begin() - if err != nil { - t.Fatal(errors.Wrap(err, "beginning a transaction")) - } - - err = lm9.run(ctx, tx) - if err != nil { - tx.Rollback() - t.Fatal(errors.Wrap(err, "failed to run")) - } - - tx.Commit() - - // Test - - // assert that note_fts was populated with correct values - var noteFtsCount int - testutils.MustScan(t, "counting note_fts", db.QueryRow("SELECT count(*) FROM note_fts;"), ¬eFtsCount) - testutils.AssertEqual(t, noteFtsCount, 2, "noteFtsCount mismatch") - - var resCount int - testutils.MustScan(t, "counting result", db.QueryRow("SELECT count(*) FROM note_fts WHERE note_fts MATCH ?", "n1"), &resCount) - testutils.AssertEqual(t, resCount, 1, "noteFtsCount mismatch") -} - -func TestLocalMigration10(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/local-10-pre-schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - - b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "123") - b2UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 2", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "123 javascript") - b3UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 3", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b3UUID, "foo") - b4UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 4", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b4UUID, "+123") - b5UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 5", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b5UUID, "0123") - b6UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 6", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b6UUID, "javascript 123") - b7UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 7", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b7UUID, "123 (1)") - b8UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 8", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b8UUID, "5") - - // Execute - tx, err := db.Begin() - if err != nil { - t.Fatal(errors.Wrap(err, "beginning a transaction")) - } - - err = lm10.run(ctx, tx) - if err != nil { - tx.Rollback() - t.Fatal(errors.Wrap(err, "failed to run")) - } - - tx.Commit() - - // Test - - // assert that note_fts was populated with correct values - var b1Label, b2Label, b3Label, b4Label, b5Label, b6Label, b7Label, b8Label string - var b1Dirty, b2Dirty, b3Dirty, b4Dirty, b5Dirty, b6Dirty, b7Dirty, b8Dirty bool - - testutils.MustScan(t, "getting b1", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty) - testutils.MustScan(t, "getting b2", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b2UUID), &b2Label, &b2Dirty) - testutils.MustScan(t, "getting b3", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b3UUID), &b3Label, &b3Dirty) - testutils.MustScan(t, "getting b4", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b4UUID), &b4Label, &b4Dirty) - testutils.MustScan(t, "getting b5", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b5UUID), &b5Label, &b5Dirty) - testutils.MustScan(t, "getting b6", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b6UUID), &b6Label, &b6Dirty) - testutils.MustScan(t, "getting b7", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b7UUID), &b7Label, &b7Dirty) - testutils.MustScan(t, "getting b8", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b8UUID), &b8Label, &b8Dirty) - - testutils.AssertEqual(t, b1Label, "123 (2)", "b1Label mismatch") - testutils.AssertEqual(t, b1Dirty, true, "b1Dirty mismatch") - testutils.AssertEqual(t, b2Label, "123 javascript", "b2Label mismatch") - testutils.AssertEqual(t, b2Dirty, false, "b2Dirty mismatch") - testutils.AssertEqual(t, b3Label, "foo", "b3Label mismatch") - testutils.AssertEqual(t, b3Dirty, false, "b3Dirty mismatch") - testutils.AssertEqual(t, b4Label, "+123", "b4Label mismatch") - testutils.AssertEqual(t, b4Dirty, false, "b4Dirty mismatch") - testutils.AssertEqual(t, b5Label, "0123 (1)", "b5Label mismatch") - testutils.AssertEqual(t, b5Dirty, true, "b5Dirty mismatch") - testutils.AssertEqual(t, b6Label, "javascript 123", "b6Label mismatch") - testutils.AssertEqual(t, b6Dirty, false, "b6Dirty mismatch") - testutils.AssertEqual(t, b7Label, "123 (1)", "b7Label mismatch") - testutils.AssertEqual(t, b7Dirty, false, "b7Dirty mismatch") - testutils.AssertEqual(t, b8Label, "5 (1)", "b8Label mismatch") - testutils.AssertEqual(t, b8Dirty, true, "b8Dirty mismatch") -} - -func TestLocalMigration11(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/local-11-pre-schema.sql", false) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - - b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "foo") - b2UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 2", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "bar baz") - b3UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 3", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b3UUID, "quz qux") - b4UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 4", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b4UUID, "quz_qux") - b5UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 5", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b5UUID, "foo bar baz quz 123") - b6UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 6", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b6UUID, "foo_bar baz") - b7UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 7", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b7UUID, "cool ideas") - b8UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 8", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b8UUID, "cool_ideas") - b9UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book 9", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b9UUID, "cool_ideas_2") - - // Execute - tx, err := db.Begin() - if err != nil { - t.Fatal(errors.Wrap(err, "beginning a transaction")) - } - - err = lm11.run(ctx, tx) - if err != nil { - tx.Rollback() - t.Fatal(errors.Wrap(err, "failed to run")) - } - - tx.Commit() - - // Test - var bookCount int - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqual(t, bookCount, 9, "bookCount mismatch") - - // assert that note_fts was populated with correct values - var b1Label, b2Label, b3Label, b4Label, b5Label, b6Label, b7Label, b8Label, b9Label string - var b1Dirty, b2Dirty, b3Dirty, b4Dirty, b5Dirty, b6Dirty, b7Dirty, b8Dirty, b9Dirty bool - - testutils.MustScan(t, "getting b1", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty) - testutils.MustScan(t, "getting b2", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b2UUID), &b2Label, &b2Dirty) - testutils.MustScan(t, "getting b3", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b3UUID), &b3Label, &b3Dirty) - testutils.MustScan(t, "getting b4", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b4UUID), &b4Label, &b4Dirty) - testutils.MustScan(t, "getting b5", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b5UUID), &b5Label, &b5Dirty) - testutils.MustScan(t, "getting b6", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b6UUID), &b6Label, &b6Dirty) - testutils.MustScan(t, "getting b7", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b7UUID), &b7Label, &b7Dirty) - testutils.MustScan(t, "getting b8", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b8UUID), &b8Label, &b8Dirty) - testutils.MustScan(t, "getting b9", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b9UUID), &b9Label, &b9Dirty) - - testutils.AssertEqual(t, b1Label, "foo", "b1Label mismatch") - testutils.AssertEqual(t, b1Dirty, false, "b1Dirty mismatch") - testutils.AssertEqual(t, b2Label, "bar_baz", "b2Label mismatch") - testutils.AssertEqual(t, b2Dirty, true, "b2Dirty mismatch") - testutils.AssertEqual(t, b3Label, "quz_qux_2", "b3Label mismatch") - testutils.AssertEqual(t, b3Dirty, true, "b3Dirty mismatch") - testutils.AssertEqual(t, b4Label, "quz_qux", "b4Label mismatch") - testutils.AssertEqual(t, b4Dirty, false, "b4Dirty mismatch") - testutils.AssertEqual(t, b5Label, "foo_bar_baz_quz_123", "b5Label mismatch") - testutils.AssertEqual(t, b5Dirty, true, "b5Dirty mismatch") - testutils.AssertEqual(t, b6Label, "foo_bar_baz", "b6Label mismatch") - testutils.AssertEqual(t, b6Dirty, true, "b6Dirty mismatch") - testutils.AssertEqual(t, b7Label, "cool_ideas_3", "b7Label mismatch") - testutils.AssertEqual(t, b7Dirty, true, "b7Dirty mismatch") - testutils.AssertEqual(t, b8Label, "cool_ideas", "b8Label mismatch") - testutils.AssertEqual(t, b8Dirty, false, "b8Dirty mismatch") - testutils.AssertEqual(t, b9Label, "cool_ideas_2", "b9Label mismatch") - testutils.AssertEqual(t, b9Dirty, false, "b9Dirty mismatch") -} - -func TestRemoteMigration1(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/remote-1-pre-schema.sql", false) - testutils.Login(t, &ctx) - defer testutils.TeardownEnv(ctx) - - JSBookUUID := "existing-js-book-uuid" - CSSBookUUID := "existing-css-book-uuid" - linuxBookUUID := "existing-linux-book-uuid" - newJSBookUUID := "new-js-book-uuid" - newCSSBookUUID := "new-css-book-uuid" - - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.String() == "/v1/books" { - res := []struct { - UUID string `json:"uuid"` - Label string `json:"label"` - }{ - { - UUID: newJSBookUUID, - Label: "js", - }, - { - UUID: newCSSBookUUID, - Label: "css", - }, - // book that only exists on the server. client must ignore. - { - UUID: "golang-book-uuid", - Label: "golang", - }, - } - - if err := json.NewEncoder(w).Encode(res); err != nil { - t.Fatal(errors.Wrap(err, "encoding response")) - } - } - })) - defer server.Close() - - ctx.APIEndpoint = server.URL - - db := ctx.DB - - testutils.MustExec(t, "inserting js book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", JSBookUUID, "js") - testutils.MustExec(t, "inserting css book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", CSSBookUUID, "css") - testutils.MustExec(t, "inserting linux book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", linuxBookUUID, "linux") - testutils.MustExec(t, "inserting sessionKey", db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemSessionKey, "someSessionKey") - testutils.MustExec(t, "inserting sessionKeyExpiry", db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemSessionKeyExpiry, time.Now().Add(24*time.Hour).Unix()) - - tx, err := db.Begin() - if err != nil { - t.Fatal(errors.Wrap(err, "beginning a transaction")) - } - - err = rm1.run(ctx, tx) - if err != nil { - tx.Rollback() - t.Fatal(errors.Wrap(err, "failed to run")) - } - - tx.Commit() - - // test - var postJSBookUUID, postCSSBookUUID, postLinuxBookUUID string - testutils.MustScan(t, "getting js book uuid", db.QueryRow("SELECT uuid FROM books WHERE label = ?", "js"), &postJSBookUUID) - testutils.MustScan(t, "getting css book uuid", db.QueryRow("SELECT uuid FROM books WHERE label = ?", "css"), &postCSSBookUUID) - testutils.MustScan(t, "getting linux book uuid", db.QueryRow("SELECT uuid FROM books WHERE label = ?", "linux"), &postLinuxBookUUID) - - testutils.AssertEqual(t, postJSBookUUID, newJSBookUUID, "js book uuid was not updated correctly") - testutils.AssertEqual(t, postCSSBookUUID, newCSSBookUUID, "css book uuid was not updated correctly") - testutils.AssertEqual(t, postLinuxBookUUID, linuxBookUUID, "linux book uuid changed") -} diff --git a/cli/testutils/fixtures/schema.sql b/cli/testutils/fixtures/schema.sql deleted file mode 100644 index a9679e45..00000000 --- a/cli/testutils/fixtures/schema.sql +++ /dev/null @@ -1,50 +0,0 @@ -CREATE TABLE books - ( - uuid text PRIMARY KEY, - label text NOT NULL - , dirty bool DEFAULT false, usn int DEFAULT 0 NOT NULL, deleted bool DEFAULT false); -CREATE TABLE system - ( - key string NOT NULL, - value text NOT NULL - ); -CREATE UNIQUE INDEX idx_books_label ON books(label); -CREATE UNIQUE INDEX idx_books_uuid ON books(uuid); -CREATE TABLE IF NOT EXISTS "notes" - ( - uuid text NOT NULL, - book_uuid text NOT NULL, - body text NOT NULL, - added_on integer NOT NULL, - edited_on integer DEFAULT 0, - public bool DEFAULT false, - dirty bool DEFAULT false, - usn int DEFAULT 0 NOT NULL, - deleted bool DEFAULT false - ); -CREATE VIRTUAL TABLE note_fts USING fts5(content=notes, body, tokenize="porter unicode61 categories 'L* N* Co Ps Pe'") -/* note_fts(body) */; -CREATE TABLE IF NOT EXISTS 'note_fts_data'(id INTEGER PRIMARY KEY, block BLOB); -CREATE TABLE IF NOT EXISTS 'note_fts_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID; -CREATE TABLE IF NOT EXISTS 'note_fts_docsize'(id INTEGER PRIMARY KEY, sz BLOB); -CREATE TABLE IF NOT EXISTS 'note_fts_config'(k PRIMARY KEY, v) WITHOUT ROWID; -CREATE TRIGGER notes_after_insert AFTER INSERT ON notes BEGIN - INSERT INTO note_fts(rowid, body) VALUES (new.rowid, new.body); - END; -CREATE TRIGGER notes_after_delete AFTER DELETE ON notes BEGIN - INSERT INTO note_fts(note_fts, rowid, body) VALUES ('delete', old.rowid, old.body); - END; -CREATE TRIGGER notes_after_update AFTER UPDATE ON notes BEGIN - INSERT INTO note_fts(note_fts, rowid, body) VALUES ('delete', old.rowid, old.body); - INSERT INTO note_fts(rowid, body) VALUES (new.rowid, new.body); - END; -CREATE TABLE actions - ( - uuid text PRIMARY KEY, - schema integer NOT NULL, - type text NOT NULL, - data text NOT NULL, - timestamp integer NOT NULL - ); -CREATE UNIQUE INDEX idx_notes_uuid ON notes(uuid); -CREATE INDEX idx_notes_book_uuid ON notes(book_uuid); diff --git a/cli/testutils/main.go b/cli/testutils/main.go deleted file mode 100644 index 15a59674..00000000 --- a/cli/testutils/main.go +++ /dev/null @@ -1,363 +0,0 @@ -/* Copyright (C) 2019 Monomax Software Pty Ltd - * - * This file is part of Dnote CLI. - * - * Dnote CLI 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 CLI 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 CLI. If not, see . - */ - -// Package testutils provides utilities used in tests -package testutils - -import ( - "bytes" - "database/sql" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "reflect" - "strings" - "testing" - "time" - - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/utils" - "github.com/pkg/errors" -) - -// InitEnv sets up a test env and returns a new dnote context -func InitEnv(t *testing.T, dnotehomePath string, fixturePath string, migrated bool) infra.DnoteCtx { - os.Setenv("DNOTE_HOME_DIR", dnotehomePath) - ctx, err := infra.NewCtx("", "") - if err != nil { - t.Fatal(errors.Wrap(err, "getting new ctx")) - } - - // set up directory - if err := os.MkdirAll(ctx.DnoteDir, 0755); err != nil { - t.Fatal(err) - } - - // set up db - b := ReadFileAbs(fixturePath) - setupSQL := string(b) - - db := ctx.DB - if _, err := db.Exec(setupSQL); err != nil { - t.Fatal(errors.Wrap(err, "running schema sql")) - } - - if migrated { - // mark migrations as done. When adding new migrations, bump the numbers here. - if _, err := db.Exec("INSERT INTO system (key, value) VALUES (? , ?);", infra.SystemSchema, 11); err != nil { - t.Fatal(errors.Wrap(err, "inserting schema")) - } - - if _, err := db.Exec("INSERT INTO system (key, value) VALUES (? , ?);", infra.SystemRemoteSchema, 1); err != nil { - t.Fatal(errors.Wrap(err, "inserting remote schema")) - } - } - - return ctx -} - -// Login simulates a logged in user by inserting credentials in the local database -func Login(t *testing.T, ctx *infra.DnoteCtx) { - db := ctx.DB - - MustExec(t, "inserting sessionKey", db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemSessionKey, "someSessionKey") - MustExec(t, "inserting sessionKeyExpiry", db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemSessionKeyExpiry, time.Now().Add(24*time.Hour).Unix()) - MustExec(t, "inserting cipherKey", db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemCipherKey, "QUVTMjU2S2V5LTMyQ2hhcmFjdGVyczEyMzQ1Njc4OTA=") - - ctx.SessionKey = "someSessionKey" - ctx.SessionKeyExpiry = time.Now().Add(24 * time.Hour).Unix() - ctx.CipherKey = []byte("AES256Key-32Characters1234567890") -} - -// TeardownEnv cleans up the test env represented by the given context -func TeardownEnv(ctx infra.DnoteCtx) { - ctx.DB.Close() - - if err := os.RemoveAll(ctx.DnoteDir); err != nil { - panic(err) - } -} - -// CopyFixture writes the content of the given fixture to the filename inside the dnote dir -func CopyFixture(ctx infra.DnoteCtx, fixturePath string, filename string) { - fp, err := filepath.Abs(fixturePath) - if err != nil { - panic(err) - } - dp, err := filepath.Abs(filepath.Join(ctx.DnoteDir, filename)) - if err != nil { - panic(err) - } - - err = utils.CopyFile(fp, dp) - if err != nil { - panic(err) - } -} - -// WriteFile writes a file with the given content and filename inside the dnote dir -func WriteFile(ctx infra.DnoteCtx, content []byte, filename string) { - dp, err := filepath.Abs(filepath.Join(ctx.DnoteDir, filename)) - if err != nil { - panic(err) - } - - if err := ioutil.WriteFile(dp, content, 0644); err != nil { - panic(err) - } -} - -// ReadFile reads the content of the file with the given name in dnote dir -func ReadFile(ctx infra.DnoteCtx, filename string) []byte { - path := filepath.Join(ctx.DnoteDir, filename) - - b, err := ioutil.ReadFile(path) - if err != nil { - panic(err) - } - - return b -} - -// ReadFileAbs reads the content of the file with the given file path by resolving -// it as an absolute path -func ReadFileAbs(relpath string) []byte { - fp, err := filepath.Abs(relpath) - if err != nil { - panic(err) - } - - b, err := ioutil.ReadFile(fp) - if err != nil { - panic(err) - } - - return b -} - -func checkEqual(a interface{}, b interface{}, message string) (bool, string) { - if a == b { - return true, "" - } - - var m string - if len(message) == 0 { - m = fmt.Sprintf("%v != %v", a, b) - } else { - m = message - } - errorMessage := fmt.Sprintf("%s.\n==== Actual ====\n%+v\n=============\n==== Expected ====\n%+v\n=================", m, a, b) - - return false, errorMessage -} - -// AssertEqual errors a test if the actual does not match the expected -func AssertEqual(t *testing.T, a interface{}, b interface{}, message string) { - ok, m := checkEqual(a, b, message) - if !ok { - t.Error(m) - } -} - -// AssertEqualf fails a test if the actual does not match the expected -func AssertEqualf(t *testing.T, a interface{}, b interface{}, message string) { - ok, m := checkEqual(a, b, message) - if !ok { - t.Fatal(m) - } -} - -// AssertNotEqual fails a test if the actual matches the expected -func AssertNotEqual(t *testing.T, a interface{}, b interface{}, message string) { - if a != b { - return - } - if len(message) == 0 { - message = fmt.Sprintf("%v == %v", a, b) - } - t.Errorf("%s. Actual: %+v. Expected: %+v.", message, a, b) -} - -// AssertDeepEqual fails a test if the actual does not deeply equal the expected -func AssertDeepEqual(t *testing.T, a interface{}, b interface{}, message string) { - if reflect.DeepEqual(a, b) { - return - } - - if len(message) == 0 { - message = fmt.Sprintf("%v != %v", a, b) - } - t.Errorf("%s.\nActual: %+v.\nExpected: %+v.", message, a, b) -} - -// ReadJSON reads JSON fixture to the struct at the destination address -func ReadJSON(path string, destination interface{}) { - var dat []byte - dat, err := ioutil.ReadFile(path) - if err != nil { - panic(errors.Wrap(err, "Failed to load fixture payload")) - } - if err := json.Unmarshal(dat, destination); err != nil { - panic(errors.Wrap(err, "Failed to get event")) - } -} - -// IsEqualJSON deeply compares two JSON byte slices -func IsEqualJSON(s1, s2 []byte) (bool, error) { - var o1 interface{} - var o2 interface{} - - if err := json.Unmarshal(s1, &o1); err != nil { - return false, errors.Wrap(err, "unmarshalling first JSON") - } - if err := json.Unmarshal(s2, &o2); err != nil { - return false, errors.Wrap(err, "unmarshalling second JSON") - } - - return reflect.DeepEqual(o1, o2), nil -} - -// MustExec executes the given SQL query and fails a test if an error occurs -func MustExec(t *testing.T, message string, db *infra.DB, query string, args ...interface{}) sql.Result { - result, err := db.Exec(query, args...) - if err != nil { - t.Fatal(errors.Wrap(errors.Wrap(err, "executing sql"), message)) - } - - return result -} - -// MustScan scans the given row and fails a test in case of any errors -func MustScan(t *testing.T, message string, row *sql.Row, args ...interface{}) { - err := row.Scan(args...) - if err != nil { - t.Fatal(errors.Wrap(errors.Wrap(err, "scanning a row"), message)) - } -} - -// NewDnoteCmd returns a new Dnote command and a pointer to stderr -func NewDnoteCmd(ctx infra.DnoteCtx, binaryName string, arg ...string) (*exec.Cmd, *bytes.Buffer, *bytes.Buffer, error) { - var stderr, stdout bytes.Buffer - - binaryPath, err := filepath.Abs(binaryName) - if err != nil { - return &exec.Cmd{}, &stderr, &stdout, errors.Wrap(err, "getting the absolute path to the test binary") - } - - cmd := exec.Command(binaryPath, arg...) - cmd.Env = []string{fmt.Sprintf("DNOTE_DIR=%s", ctx.DnoteDir), fmt.Sprintf("DNOTE_HOME_DIR=%s", ctx.HomeDir)} - cmd.Stderr = &stderr - cmd.Stdout = &stdout - - return cmd, &stderr, &stdout, nil -} - -// RunDnoteCmd runs a dnote command -func RunDnoteCmd(t *testing.T, ctx infra.DnoteCtx, binaryName string, arg ...string) { - t.Logf("running: %s %s", binaryName, strings.Join(arg, " ")) - - cmd, stderr, stdout, err := NewDnoteCmd(ctx, binaryName, arg...) - if err != nil { - t.Logf("\n%s", stdout) - t.Fatal(errors.Wrap(err, "getting command").Error()) - } - - cmd.Env = append(cmd.Env, "DNOTE_DEBUG=1") - - if err := cmd.Run(); err != nil { - t.Logf("\n%s", stdout) - t.Fatal(errors.Wrapf(err, "running command %s", stderr.String())) - } - - // Print stdout if and only if test fails later - t.Logf("\n%s", stdout) -} - -// WaitDnoteCmd runs a dnote command and waits until the command is exited -func WaitDnoteCmd(t *testing.T, ctx infra.DnoteCtx, runFunc func(io.WriteCloser) error, binaryName string, arg ...string) { - t.Logf("running: %s %s", binaryName, strings.Join(arg, " ")) - - cmd, stderr, stdout, err := NewDnoteCmd(ctx, binaryName, arg...) - if err != nil { - t.Logf("\n%s", stdout) - t.Fatal(errors.Wrap(err, "getting command").Error()) - } - - stdin, err := cmd.StdinPipe() - if err != nil { - t.Logf("\n%s", stdout) - t.Fatal(errors.Wrap(err, "getting stdin %s")) - } - defer stdin.Close() - - // Start the program - err = cmd.Start() - if err != nil { - t.Logf("\n%s", stdout) - t.Fatal(errors.Wrap(err, "starting command")) - } - - err = runFunc(stdin) - if err != nil { - t.Logf("\n%s", stdout) - t.Fatal(errors.Wrap(err, "running with stdin")) - } - - err = cmd.Wait() - if err != nil { - t.Logf("\n%s", stdout) - t.Fatal(errors.Wrapf(err, "running command %s", stderr.String())) - } - - // Print stdout if and only if test fails later - t.Logf("\n%s", stdout) -} - -// UserConfirm simulates confirmation from the user by writing to stdin -func UserConfirm(stdin io.WriteCloser) error { - // confirm - if _, err := io.WriteString(stdin, "y\n"); err != nil { - return errors.Wrap(err, "indicating confirmation in stdin") - } - - return nil -} - -// MustMarshalJSON marshalls the given interface into JSON. -// If there is any error, it fails the test. -func MustMarshalJSON(t *testing.T, v interface{}) []byte { - b, err := json.Marshal(v) - if err != nil { - t.Fatalf("%s: marshalling data", t.Name()) - } - - return b -} - -// MustUnmarshalJSON marshalls the given interface into JSON. -// If there is any error, it fails the test. -func MustUnmarshalJSON(t *testing.T, data []byte, v interface{}) { - err := json.Unmarshal(data, v) - if err != nil { - t.Fatalf("%s: unmarshalling data", t.Name()) - } -} diff --git a/cli/testutils/setup.go b/cli/testutils/setup.go deleted file mode 100644 index 73b21d8a..00000000 --- a/cli/testutils/setup.go +++ /dev/null @@ -1,79 +0,0 @@ -/* Copyright (C) 2019 Monomax Software Pty Ltd - * - * This file is part of Dnote CLI. - * - * Dnote CLI 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 CLI 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 CLI. If not, see . - */ - -package testutils - -import ( - "github.com/dnote/dnote/cli/infra" - "testing" -) - -// Setup1 sets up a dnote env #1 -// dnote4.json -func Setup1(t *testing.T, ctx infra.DnoteCtx) { - db := ctx.DB - - b1UUID := "js-book-uuid" - b2UUID := "linux-book-uuid" - - MustExec(t, "setting up book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "js") - MustExec(t, "setting up book 2", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "linux") - - MustExec(t, "setting up note 1", db, "INSERT INTO notes (uuid, book_uuid, body, added_on) VALUES (?, ?, ?, ?)", "43827b9a-c2b0-4c06-a290-97991c896653", b1UUID, "Booleans have toString()", 1515199943) -} - -// Setup2 sets up a dnote env #2 -// dnote3.json -func Setup2(t *testing.T, ctx infra.DnoteCtx) { - db := ctx.DB - - b1UUID := "js-book-uuid" - b2UUID := "linux-book-uuid" - - MustExec(t, "setting up book 1", db, "INSERT INTO books (uuid, label, usn) VALUES (?, ?, ?)", b1UUID, "js", 111) - MustExec(t, "setting up book 2", db, "INSERT INTO books (uuid, label, usn) VALUES (?, ?, ?)", b2UUID, "linux", 122) - - MustExec(t, "setting up note 1", db, "INSERT INTO notes (uuid, book_uuid, body, added_on, usn) VALUES (?, ?, ?, ?, ?)", "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", b1UUID, "n1 body", 1515199951, 11) - MustExec(t, "setting up note 2", db, "INSERT INTO notes (uuid, book_uuid, body, added_on, usn) VALUES (?, ?, ?, ?, ?)", "43827b9a-c2b0-4c06-a290-97991c896653", b1UUID, "n2 body", 1515199943, 12) - MustExec(t, "setting up note 3", db, "INSERT INTO notes (uuid, book_uuid, body, added_on, usn) VALUES (?, ?, ?, ?, ?)", "3e065d55-6d47-42f2-a6bf-f5844130b2d2", b2UUID, "n3 body", 1515199961, 13) -} - -// Setup3 sets up a dnote env #1 -// dnote1.json -func Setup3(t *testing.T, ctx infra.DnoteCtx) { - db := ctx.DB - - b1UUID := "js-book-uuid" - - MustExec(t, "setting up book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "js") - - MustExec(t, "setting up note 1", db, "INSERT INTO notes (uuid, book_uuid, body, added_on) VALUES (?, ?, ?, ?)", "43827b9a-c2b0-4c06-a290-97991c896653", b1UUID, "Booleans have toString()", 1515199943) -} - -// Setup4 sets up a dnote env #1 -// dnote2.json -func Setup4(t *testing.T, ctx infra.DnoteCtx) { - db := ctx.DB - - b1UUID := "js-book-uuid" - - MustExec(t, "setting up book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "js") - - MustExec(t, "setting up note 1", db, "INSERT INTO notes (rowid, uuid, book_uuid, body, added_on) VALUES (?, ?, ?, ?, ?)", 1, "43827b9a-c2b0-4c06-a290-97991c896653", b1UUID, "Booleans have toString()", 1515199943) - MustExec(t, "setting up note 2", db, "INSERT INTO notes (rowid, uuid, book_uuid, body, added_on) VALUES (?, ?, ?, ?, ?)", 2, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", b1UUID, "Date object implements mathematical comparisons", 1515199951) -} diff --git a/pkg/assert/assert.go b/pkg/assert/assert.go new file mode 100644 index 00000000..7ef193e0 --- /dev/null +++ b/pkg/assert/assert.go @@ -0,0 +1,123 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +// Package assert provides functions to assert a condition in tests +package assert + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "reflect" + "testing" + + "github.com/pkg/errors" +) + +func checkEqual(a interface{}, b interface{}, message string) (bool, string) { + if a == b { + return true, "" + } + + var m string + if len(message) == 0 { + m = fmt.Sprintf("%v != %v", a, b) + } else { + m = message + } + errorMessage := fmt.Sprintf("%s.\n==== Actual ====\n%+v\n=============\n==== Expected ====\n%+v\n=================", m, a, b) + + return false, errorMessage +} + +// Equal errors a test if the actual does not match the expected +func Equal(t *testing.T, a interface{}, b interface{}, message string) { + ok, m := checkEqual(a, b, message) + if !ok { + t.Error(m) + } +} + +// Equalf fails a test if the actual does not match the expected +func Equalf(t *testing.T, a interface{}, b interface{}, message string) { + ok, m := checkEqual(a, b, message) + if !ok { + t.Fatal(m) + } +} + +// NotEqual fails a test if the actual matches the expected +func NotEqual(t *testing.T, a interface{}, b interface{}, message string) { + if a != b { + return + } + if len(message) == 0 { + message = fmt.Sprintf("%v == %v", a, b) + } + t.Errorf("%s. Actual: %+v. Expected: %+v.", message, a, b) +} + +// DeepEqual fails a test if the actual does not deeply equal the expected +func DeepEqual(t *testing.T, a interface{}, b interface{}, message string) { + if reflect.DeepEqual(a, b) { + return + } + + if len(message) == 0 { + message = fmt.Sprintf("%v != %v", a, b) + } + t.Errorf("%s.\nActual: %+v.\nExpected: %+v.", message, a, b) +} + +// EqualJSON asserts that two JSON strings are equal +func EqualJSON(t *testing.T, a, b, message string) { + var o1 interface{} + var o2 interface{} + + err := json.Unmarshal([]byte(a), &o1) + if err != nil { + panic(fmt.Errorf("Error mashalling string 1 :: %s", err.Error())) + } + err = json.Unmarshal([]byte(b), &o2) + if err != nil { + panic(fmt.Errorf("Error mashalling string 2 :: %s", err.Error())) + } + + if reflect.DeepEqual(o1, o2) { + return + } + + if len(message) == 0 { + message = fmt.Sprintf("%v != %v", a, b) + } + t.Errorf("%s.\nActual: %+v.\nExpected: %+v.", message, a, b) +} + +// StatusCodeEquals asserts that the reponse's status code is equal to the +// expected +func StatusCodeEquals(t *testing.T, res *http.Response, expected int, message string) { + if res.StatusCode != expected { + body, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(errors.Wrap(err, "reading body")) + } + + t.Errorf("status code mismatch. %s: got %v want %v. Message was: '%s'", message, res.StatusCode, expected, string(body)) + } +} diff --git a/cli/.gitignore b/pkg/cli/.gitignore similarity index 100% rename from cli/.gitignore rename to pkg/cli/.gitignore diff --git a/cli/COMMANDS.md b/pkg/cli/COMMANDS.md similarity index 100% rename from cli/COMMANDS.md rename to pkg/cli/COMMANDS.md diff --git a/cli/Gopkg.lock b/pkg/cli/Gopkg.lock similarity index 87% rename from cli/Gopkg.lock rename to pkg/cli/Gopkg.lock index 9db5f4ed..9598c01b 100644 --- a/cli/Gopkg.lock +++ b/pkg/cli/Gopkg.lock @@ -17,6 +17,14 @@ revision = "5b77d2a35fb0ede96d138fc9a99f5c9b6aef11b4" version = "v1.7.0" +[[projects]] + branch = "endpoint-186" + digest = "1:b56e65cab256427453f4454af3e7be2c1c544b52332772dbf7e6992aa7fdf599" + name = "github.com/dnote/dnote" + packages = ["pkg/assert"] + pruneopts = "" + revision = "54646c6bf85fba5efb8b4de67c9fed19cdbe0d97" + [[projects]] branch = "master" digest = "1:fe99ddb68e996f2f9f7995e9765bc283ceef12dbe30de17922900c1cfa9dfc09" @@ -81,6 +89,14 @@ revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" version = "v1.2.0" +[[projects]] + digest = "1:3962f553b77bf6c03fc07cd687a22dd3b00fe11aa14d31194f5505f5bb65cdc8" + name = "github.com/sergi/go-diff" + packages = ["diffmatchpatch"] + pruneopts = "" + revision = "1744e2970ca51c86172c8190fadad617561ed6e7" + version = "v1.0.0" + [[projects]] digest = "1:78715f4ed019d19795e67eed1dc63f525461d925616b1ed02b72582c01362440" name = "github.com/spf13/cobra" @@ -134,10 +150,12 @@ input-imports = [ "github.com/dnote/actions", "github.com/dnote/color", + "github.com/dnote/dnote/pkg/assert", "github.com/google/go-github/github", "github.com/mattn/go-sqlite3", "github.com/pkg/errors", "github.com/satori/go.uuid", + "github.com/sergi/go-diff/diffmatchpatch", "github.com/spf13/cobra", "golang.org/x/crypto/hkdf", "golang.org/x/crypto/pbkdf2", diff --git a/cli/Gopkg.toml b/pkg/cli/Gopkg.toml similarity index 100% rename from cli/Gopkg.toml rename to pkg/cli/Gopkg.toml diff --git a/cli/Makefile b/pkg/cli/Makefile similarity index 100% rename from cli/Makefile rename to pkg/cli/Makefile diff --git a/cli/README.md b/pkg/cli/README.md similarity index 100% rename from cli/README.md rename to pkg/cli/README.md diff --git a/cli/client/client.go b/pkg/cli/client/client.go similarity index 91% rename from cli/client/client.go rename to pkg/cli/client/client.go index 8697adc5..d3f81a76 100644 --- a/cli/client/client.go +++ b/pkg/cli/client/client.go @@ -30,8 +30,8 @@ import ( "strings" "time" - "github.com/dnote/dnote/cli/crypt" - "github.com/dnote/dnote/cli/infra" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/crypt" "github.com/pkg/errors" ) @@ -43,7 +43,7 @@ type requestOptions struct { HTTPClient *http.Client } -func getReq(ctx infra.DnoteCtx, path, method, body string) (*http.Request, error) { +func getReq(ctx context.DnoteCtx, path, method, body string) (*http.Request, error) { endpoint := fmt.Sprintf("%s%s", ctx.APIEndpoint, path) req, err := http.NewRequest(method, endpoint, strings.NewReader(body)) if err != nil { @@ -77,7 +77,7 @@ func checkRespErr(res *http.Response) error { } // doReq does a http request to the given path in the api endpoint -func doReq(ctx infra.DnoteCtx, method, path, body string, options *requestOptions) (*http.Response, error) { +func doReq(ctx context.DnoteCtx, method, path, body string, options *requestOptions) (*http.Response, error) { req, err := getReq(ctx, path, method, body) if err != nil { return nil, errors.Wrap(err, "getting request") @@ -104,7 +104,7 @@ func doReq(ctx infra.DnoteCtx, method, path, body string, options *requestOption // doAuthorizedReq does a http request to the given path in the api endpoint as a user, // with the appropriate headers. The given path should include the preceding slash. -func doAuthorizedReq(ctx infra.DnoteCtx, method, path, body string, options *requestOptions) (*http.Response, error) { +func doAuthorizedReq(ctx context.DnoteCtx, method, path, body string, options *requestOptions) (*http.Response, error) { if ctx.SessionKey == "" { return nil, errors.New("no session key found") } @@ -120,7 +120,7 @@ type GetSyncStateResp struct { } // GetSyncState gets the sync state response from the server -func GetSyncState(ctx infra.DnoteCtx) (GetSyncStateResp, error) { +func GetSyncState(ctx context.DnoteCtx) (GetSyncStateResp, error) { var ret GetSyncStateResp res, err := doAuthorizedReq(ctx, "GET", "/v1/sync/state", "", nil) @@ -184,7 +184,7 @@ type GetSyncFragmentResp struct { } // GetSyncFragment gets a sync fragment response from the server -func GetSyncFragment(ctx infra.DnoteCtx, afterUSN int) (GetSyncFragmentResp, error) { +func GetSyncFragment(ctx context.DnoteCtx, afterUSN int) (GetSyncFragmentResp, error) { v := url.Values{} v.Set("after_usn", strconv.Itoa(afterUSN)) queryStr := v.Encode() @@ -226,7 +226,7 @@ type CreateBookResp struct { } // CreateBook creates a new book in the server -func CreateBook(ctx infra.DnoteCtx, label string) (CreateBookResp, error) { +func CreateBook(ctx context.DnoteCtx, label string) (CreateBookResp, error) { encLabel, err := crypt.AesGcmEncrypt(ctx.CipherKey, []byte(label)) if err != nil { return CreateBookResp{}, errors.Wrap(err, "encrypting the label") @@ -263,7 +263,7 @@ type UpdateBookResp struct { } // UpdateBook updates a book in the server -func UpdateBook(ctx infra.DnoteCtx, label, uuid string) (UpdateBookResp, error) { +func UpdateBook(ctx context.DnoteCtx, label, uuid string) (UpdateBookResp, error) { encName, err := crypt.AesGcmEncrypt(ctx.CipherKey, []byte(label)) if err != nil { return UpdateBookResp{}, errors.Wrap(err, "encrypting the content") @@ -298,7 +298,7 @@ type DeleteBookResp struct { } // DeleteBook deletes a book in the server -func DeleteBook(ctx infra.DnoteCtx, uuid string) (DeleteBookResp, error) { +func DeleteBook(ctx context.DnoteCtx, uuid string) (DeleteBookResp, error) { endpoint := fmt.Sprintf("/v1/books/%s", uuid) res, err := doAuthorizedReq(ctx, "DELETE", endpoint, "", nil) if err != nil { @@ -347,7 +347,7 @@ type RespNote struct { } // CreateNote creates a note in the server -func CreateNote(ctx infra.DnoteCtx, bookUUID, content string) (CreateNoteResp, error) { +func CreateNote(ctx context.DnoteCtx, bookUUID, content string) (CreateNoteResp, error) { encBody, err := crypt.AesGcmEncrypt(ctx.CipherKey, []byte(content)) if err != nil { return CreateNoteResp{}, errors.Wrap(err, "encrypting the content") @@ -388,7 +388,7 @@ type UpdateNoteResp struct { } // UpdateNote updates a note in the server -func UpdateNote(ctx infra.DnoteCtx, uuid, bookUUID, content string, public bool) (UpdateNoteResp, error) { +func UpdateNote(ctx context.DnoteCtx, uuid, bookUUID, content string, public bool) (UpdateNoteResp, error) { encBody, err := crypt.AesGcmEncrypt(ctx.CipherKey, []byte(content)) if err != nil { return UpdateNoteResp{}, errors.Wrap(err, "encrypting the content") @@ -425,7 +425,7 @@ type DeleteNoteResp struct { } // DeleteNote removes a note in the server -func DeleteNote(ctx infra.DnoteCtx, uuid string) (DeleteNoteResp, error) { +func DeleteNote(ctx context.DnoteCtx, uuid string) (DeleteNoteResp, error) { endpoint := fmt.Sprintf("/v1/notes/%s", uuid) res, err := doAuthorizedReq(ctx, "DELETE", endpoint, "", nil) if err != nil { @@ -447,7 +447,7 @@ type GetBooksResp []struct { } // GetBooks gets books from the server -func GetBooks(ctx infra.DnoteCtx, sessionKey string) (GetBooksResp, error) { +func GetBooks(ctx context.DnoteCtx, sessionKey string) (GetBooksResp, error) { res, err := doAuthorizedReq(ctx, "GET", "/v1/books", "", nil) if err != nil { return GetBooksResp{}, errors.Wrap(err, "making http request") @@ -467,7 +467,7 @@ type PresigninResponse struct { } // GetPresignin gets presignin credentials -func GetPresignin(ctx infra.DnoteCtx, email string) (PresigninResponse, error) { +func GetPresignin(ctx context.DnoteCtx, email string) (PresigninResponse, error) { res, err := doReq(ctx, "GET", fmt.Sprintf("/v1/presignin?email=%s", email), "", nil) if err != nil { return PresigninResponse{}, errors.Wrap(err, "making http request") @@ -495,7 +495,7 @@ type SigninResponse struct { } // Signin requests a session token -func Signin(ctx infra.DnoteCtx, email, authKey string) (SigninResponse, error) { +func Signin(ctx context.DnoteCtx, email, authKey string) (SigninResponse, error) { payload := SigninPayload{ Email: email, AuthKey: authKey, @@ -522,7 +522,7 @@ func Signin(ctx infra.DnoteCtx, email, authKey string) (SigninResponse, error) { } // Signout deletes a user session on the server side -func Signout(ctx infra.DnoteCtx, sessionKey string) error { +func Signout(ctx context.DnoteCtx, sessionKey string) error { hc := http.Client{ // No need to follow redirect CheckRedirect: func(req *http.Request, via []*http.Request) error { diff --git a/cli/cmd/add/add.go b/pkg/cli/cmd/add/add.go similarity index 81% rename from cli/cmd/add/add.go rename to pkg/cli/cmd/add/add.go index 3eaaff69..11be9ec0 100644 --- a/cli/cmd/add/add.go +++ b/pkg/cli/cmd/add/add.go @@ -23,10 +23,14 @@ import ( "strings" "time" - "github.com/dnote/dnote/cli/core" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" - "github.com/dnote/dnote/cli/utils" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/infra" + "github.com/dnote/dnote/pkg/cli/log" + "github.com/dnote/dnote/pkg/cli/output" + "github.com/dnote/dnote/pkg/cli/ui" + "github.com/dnote/dnote/pkg/cli/upgrade" + "github.com/dnote/dnote/pkg/cli/utils" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -51,7 +55,7 @@ func preRun(cmd *cobra.Command, args []string) error { } // NewCmd returns a new add command -func NewCmd(ctx infra.DnoteCtx) *cobra.Command { +func NewCmd(ctx context.DnoteCtx) *cobra.Command { cmd := &cobra.Command{ Use: "add ", Short: "Add a new note", @@ -102,7 +106,7 @@ func validateBookName(name string) error { return nil } -func newRun(ctx infra.DnoteCtx) core.RunEFunc { +func newRun(ctx context.DnoteCtx) infra.RunEFunc { return func(cmd *cobra.Command, args []string) error { bookName := args[0] @@ -111,8 +115,8 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { } if content == "" { - fpath := core.GetDnoteTmpContentPath(ctx) - err := core.GetEditorInput(ctx, fpath, &content) + fpath := ui.GetTmpContentPath(ctx) + err := ui.GetEditorInput(ctx, fpath, &content) if err != nil { return errors.Wrap(err, "Failed to get editor input") } @@ -130,14 +134,15 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { log.Successf("added to %s\n", bookName) - info, err := core.GetNoteInfo(ctx, noteRowID) + db := ctx.DB + info, err := database.GetNoteInfo(db, noteRowID) if err != nil { return err } - core.PrintNoteInfo(info) + output.NoteInfo(info) - if err := core.CheckUpdate(ctx); err != nil { + if err := upgrade.Check(ctx); err != nil { log.Error(errors.Wrap(err, "automatically checking updates").Error()) } @@ -145,7 +150,7 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { } } -func writeNote(ctx infra.DnoteCtx, bookLabel string, content string, ts int64) (string, error) { +func writeNote(ctx context.DnoteCtx, bookLabel string, content string, ts int64) (string, error) { tx, err := ctx.DB.Begin() if err != nil { return "", errors.Wrap(err, "beginning a transaction") @@ -156,7 +161,7 @@ func writeNote(ctx infra.DnoteCtx, bookLabel string, content string, ts int64) ( if err == sql.ErrNoRows { bookUUID = utils.GenerateUUID() - b := core.NewBook(bookUUID, bookLabel, 0, false, true) + b := database.NewBook(bookUUID, bookLabel, 0, false, true) err = b.Insert(tx) if err != nil { tx.Rollback() @@ -167,7 +172,7 @@ func writeNote(ctx infra.DnoteCtx, bookLabel string, content string, ts int64) ( } noteUUID := utils.GenerateUUID() - n := core.NewNote(noteUUID, bookUUID, content, ts, 0, 0, false, false, true) + n := database.NewNote(noteUUID, bookUUID, content, ts, 0, 0, false, false, true) err = n.Insert(tx) if err != nil { diff --git a/cli/cmd/add/add_test.go b/pkg/cli/cmd/add/add_test.go similarity index 92% rename from cli/cmd/add/add_test.go rename to pkg/cli/cmd/add/add_test.go index 0b93401b..e1129a74 100644 --- a/cli/cmd/add/add_test.go +++ b/pkg/cli/cmd/add/add_test.go @@ -22,7 +22,7 @@ import ( "fmt" "testing" - "github.com/dnote/dnote/cli/testutils" + "github.com/dnote/dnote/pkg/assert" ) func TestValidateBookName(t *testing.T) { @@ -101,6 +101,6 @@ func TestValidateBookName(t *testing.T) { for _, tc := range testCases { actual := validateBookName(tc.input) - testutils.AssertEqual(t, actual, tc.expected, fmt.Sprintf("result does not match for the input '%s'", tc.input)) + assert.Equal(t, actual, tc.expected, fmt.Sprintf("result does not match for the input '%s'", tc.input)) } } diff --git a/cli/cmd/cat/cat.go b/pkg/cli/cmd/cat/cat.go similarity index 82% rename from cli/cmd/cat/cat.go rename to pkg/cli/cmd/cat/cat.go index 253fa87f..b9209083 100644 --- a/cli/cmd/cat/cat.go +++ b/pkg/cli/cmd/cat/cat.go @@ -19,9 +19,11 @@ package cat import ( - "github.com/dnote/dnote/cli/core" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/infra" + "github.com/dnote/dnote/pkg/cli/log" + "github.com/dnote/dnote/pkg/cli/output" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -45,7 +47,7 @@ func preRun(cmd *cobra.Command, args []string) error { } // NewCmd returns a new cat command -func NewCmd(ctx infra.DnoteCtx) *cobra.Command { +func NewCmd(ctx context.DnoteCtx) *cobra.Command { cmd := &cobra.Command{ Use: "cat ", Aliases: []string{"c"}, @@ -60,7 +62,7 @@ func NewCmd(ctx infra.DnoteCtx) *cobra.Command { } // NewRun returns a new run function -func NewRun(ctx infra.DnoteCtx) core.RunEFunc { +func NewRun(ctx context.DnoteCtx) infra.RunEFunc { return func(cmd *cobra.Command, args []string) error { var noteRowID string @@ -72,12 +74,13 @@ func NewRun(ctx infra.DnoteCtx) core.RunEFunc { noteRowID = args[0] } - info, err := core.GetNoteInfo(ctx, noteRowID) + db := ctx.DB + info, err := database.GetNoteInfo(db, noteRowID) if err != nil { return err } - core.PrintNoteInfo(info) + output.NoteInfo(info) return nil } diff --git a/cli/cmd/edit/edit.go b/pkg/cli/cmd/edit/edit.go similarity index 88% rename from cli/cmd/edit/edit.go rename to pkg/cli/cmd/edit/edit.go index 71b7216f..4ec1daa9 100644 --- a/cli/cmd/edit/edit.go +++ b/pkg/cli/cmd/edit/edit.go @@ -24,9 +24,10 @@ import ( "io/ioutil" "time" - "github.com/dnote/dnote/cli/core" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/infra" + "github.com/dnote/dnote/pkg/cli/log" + "github.com/dnote/dnote/pkg/cli/ui" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -41,7 +42,7 @@ var example = ` dnote edit 3 -c "new content"` // NewCmd returns a new edit command -func NewCmd(ctx infra.DnoteCtx) *cobra.Command { +func NewCmd(ctx context.DnoteCtx) *cobra.Command { cmd := &cobra.Command{ Use: "edit", Short: "Edit a note", @@ -65,7 +66,7 @@ func preRun(cmd *cobra.Command, args []string) error { return nil } -func newRun(ctx infra.DnoteCtx) core.RunEFunc { +func newRun(ctx context.DnoteCtx) infra.RunEFunc { return func(cmd *cobra.Command, args []string) error { db := ctx.DB @@ -88,14 +89,14 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { } if newContent == "" { - fpath := core.GetDnoteTmpContentPath(ctx) + fpath := ui.GetTmpContentPath(ctx) e := ioutil.WriteFile(fpath, []byte(oldContent), 0644) if e != nil { return errors.Wrap(e, "preparing tmp content file") } - e = core.GetEditorInput(ctx, fpath, &newContent) + e = ui.GetEditorInput(ctx, fpath, &newContent) if e != nil { return errors.Wrap(err, "getting editor input") } @@ -106,7 +107,7 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { } ts := time.Now().UnixNano() - newContent = core.SanitizeContent(newContent) + newContent = ui.SanitizeContent(newContent) tx, err := db.Begin() if err != nil { diff --git a/cli/cmd/find/find.go b/pkg/cli/cmd/find/find.go similarity index 93% rename from cli/cmd/find/find.go rename to pkg/cli/cmd/find/find.go index b03fc4cc..f0ec061f 100644 --- a/cli/cmd/find/find.go +++ b/pkg/cli/cmd/find/find.go @@ -23,9 +23,9 @@ import ( "fmt" "strings" - "github.com/dnote/dnote/cli/core" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/infra" + "github.com/dnote/dnote/pkg/cli/log" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -52,7 +52,7 @@ func preRun(cmd *cobra.Command, args []string) error { } // NewCmd returns a new remove command -func NewCmd(ctx infra.DnoteCtx) *cobra.Command { +func NewCmd(ctx context.DnoteCtx) *cobra.Command { cmd := &cobra.Command{ Use: "find", Short: "Find notes by keywords", @@ -130,7 +130,7 @@ func escapePhrase(s string) (string, error) { return b.String(), nil } -func doQuery(ctx infra.DnoteCtx, query, bookName string) (*sql.Rows, error) { +func doQuery(ctx context.DnoteCtx, query, bookName string) (*sql.Rows, error) { db := ctx.DB sql := `SELECT @@ -153,7 +153,7 @@ func doQuery(ctx infra.DnoteCtx, query, bookName string) (*sql.Rows, error) { return rows, err } -func newRun(ctx infra.DnoteCtx) core.RunEFunc { +func newRun(ctx context.DnoteCtx) infra.RunEFunc { return func(cmd *cobra.Command, args []string) error { phrase, err := escapePhrase(args[0]) if err != nil { diff --git a/cli/cmd/find/lexer.go b/pkg/cli/cmd/find/lexer.go similarity index 100% rename from cli/cmd/find/lexer.go rename to pkg/cli/cmd/find/lexer.go diff --git a/cli/cmd/find/lexer_test.go b/pkg/cli/cmd/find/lexer_test.go similarity index 94% rename from cli/cmd/find/lexer_test.go rename to pkg/cli/cmd/find/lexer_test.go index 55118695..db5ae7d9 100644 --- a/cli/cmd/find/lexer_test.go +++ b/pkg/cli/cmd/find/lexer_test.go @@ -22,7 +22,7 @@ import ( "fmt" "testing" - "github.com/dnote/dnote/cli/testutils" + "github.com/dnote/dnote/pkg/assert" ) func TestScanToken(t *testing.T) { @@ -117,8 +117,8 @@ func TestScanToken(t *testing.T) { t.Run(fmt.Sprintf("test case %d", tcIdx), func(t *testing.T) { tok, nextIdx := scanToken(tc.idx, tc.input) - testutils.AssertEqual(t, nextIdx, tc.retIdx, "retIdx mismatch") - testutils.AssertDeepEqual(t, tok, tc.retTok, "retTok mismatch") + assert.Equal(t, nextIdx, tc.retIdx, "retIdx mismatch") + assert.DeepEqual(t, tok, tc.retTok, "retTok mismatch") }) } } @@ -225,7 +225,7 @@ func TestTokenize(t *testing.T) { t.Run(fmt.Sprintf("test case %d", tcIdx), func(t *testing.T) { tokens := tokenize(tc.input) - testutils.AssertDeepEqual(t, tokens, tc.tokens, "tokens mismatch") + assert.DeepEqual(t, tokens, tc.tokens, "tokens mismatch") }) } } diff --git a/cli/cmd/login/login.go b/pkg/cli/cmd/login/login.go similarity index 74% rename from cli/cmd/login/login.go rename to pkg/cli/cmd/login/login.go index e2ffdf95..2a9dce39 100644 --- a/cli/cmd/login/login.go +++ b/pkg/cli/cmd/login/login.go @@ -22,12 +22,14 @@ import ( "encoding/base64" "strconv" - "github.com/dnote/dnote/cli/client" - "github.com/dnote/dnote/cli/core" - "github.com/dnote/dnote/cli/crypt" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" - "github.com/dnote/dnote/cli/utils" + "github.com/dnote/dnote/pkg/cli/client" + "github.com/dnote/dnote/pkg/cli/consts" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/crypt" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/infra" + "github.com/dnote/dnote/pkg/cli/log" + "github.com/dnote/dnote/pkg/cli/ui" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -36,7 +38,7 @@ var example = ` dnote login` // NewCmd returns a new login command -func NewCmd(ctx infra.DnoteCtx) *cobra.Command { +func NewCmd(ctx context.DnoteCtx) *cobra.Command { cmd := &cobra.Command{ Use: "login", Short: "Login to dnote server", @@ -48,7 +50,7 @@ func NewCmd(ctx infra.DnoteCtx) *cobra.Command { } // Do dervies credentials on the client side and requests a session token from the server -func Do(ctx infra.DnoteCtx, email, password string) error { +func Do(ctx context.DnoteCtx, email, password string) error { presigninResp, err := client.GetPresignin(ctx, email) if err != nil { return errors.Wrap(err, "getting presiginin") @@ -78,13 +80,13 @@ func Do(ctx infra.DnoteCtx, email, password string) error { return errors.Wrap(err, "beginning a transaction") } - if err := core.UpsertSystem(tx, infra.SystemCipherKey, cipherKeyDecB64); err != nil { + if err := database.UpsertSystem(tx, consts.SystemCipherKey, cipherKeyDecB64); err != nil { return errors.Wrap(err, "saving enc key") } - if err := core.UpsertSystem(tx, infra.SystemSessionKey, signinResp.Key); err != nil { + if err := database.UpsertSystem(tx, consts.SystemSessionKey, signinResp.Key); err != nil { return errors.Wrap(err, "saving session key") } - if err := core.UpsertSystem(tx, infra.SystemSessionKeyExpiry, strconv.FormatInt(signinResp.ExpiresAt, 10)); err != nil { + if err := database.UpsertSystem(tx, consts.SystemSessionKeyExpiry, strconv.FormatInt(signinResp.ExpiresAt, 10)); err != nil { return errors.Wrap(err, "saving session key") } @@ -93,17 +95,17 @@ func Do(ctx infra.DnoteCtx, email, password string) error { return nil } -func newRun(ctx infra.DnoteCtx) core.RunEFunc { +func newRun(ctx context.DnoteCtx) infra.RunEFunc { return func(cmd *cobra.Command, args []string) error { var email, password string - if err := utils.PromptInput("email", &email); err != nil { + if err := ui.PromptInput("email", &email); err != nil { return errors.Wrap(err, "getting email input") } if email == "" { return errors.New("Email is empty") } - if err := utils.PromptPassword("password", &password); err != nil { + if err := ui.PromptPassword("password", &password); err != nil { return errors.Wrap(err, "getting password input") } if password == "" { diff --git a/cli/cmd/logout/logout.go b/pkg/cli/cmd/logout/logout.go similarity index 74% rename from cli/cmd/logout/logout.go rename to pkg/cli/cmd/logout/logout.go index 4f8536dc..1f3888e6 100644 --- a/cli/cmd/logout/logout.go +++ b/pkg/cli/cmd/logout/logout.go @@ -21,10 +21,12 @@ package logout import ( "database/sql" - "github.com/dnote/dnote/cli/client" - "github.com/dnote/dnote/cli/core" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" + "github.com/dnote/dnote/pkg/cli/client" + "github.com/dnote/dnote/pkg/cli/consts" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/infra" + "github.com/dnote/dnote/pkg/cli/log" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -36,7 +38,7 @@ var example = ` dnote logout` // NewCmd returns a new logout command -func NewCmd(ctx infra.DnoteCtx) *cobra.Command { +func NewCmd(ctx context.DnoteCtx) *cobra.Command { cmd := &cobra.Command{ Use: "logout", Short: "Logout from the server", @@ -48,7 +50,7 @@ func NewCmd(ctx infra.DnoteCtx) *cobra.Command { } // Do performs logout -func Do(ctx infra.DnoteCtx) error { +func Do(ctx context.DnoteCtx) error { db := ctx.DB tx, err := db.Begin() if err != nil { @@ -56,7 +58,7 @@ func Do(ctx infra.DnoteCtx) error { } var key string - err = core.GetSystem(tx, infra.SystemSessionKey, &key) + err = database.GetSystem(tx, consts.SystemSessionKey, &key) if errors.Cause(err) == sql.ErrNoRows { return ErrNotLoggedIn } else if err != nil { @@ -68,13 +70,13 @@ func Do(ctx infra.DnoteCtx) error { return errors.Wrap(err, "requesting logout") } - if err := core.DeleteSystem(tx, infra.SystemCipherKey); err != nil { + if err := database.DeleteSystem(tx, consts.SystemCipherKey); err != nil { return errors.Wrap(err, "deleting enc key") } - if err := core.DeleteSystem(tx, infra.SystemSessionKey); err != nil { + if err := database.DeleteSystem(tx, consts.SystemSessionKey); err != nil { return errors.Wrap(err, "deleting session key") } - if err := core.DeleteSystem(tx, infra.SystemSessionKeyExpiry); err != nil { + if err := database.DeleteSystem(tx, consts.SystemSessionKeyExpiry); err != nil { return errors.Wrap(err, "deleting session key expiry") } @@ -83,7 +85,7 @@ func Do(ctx infra.DnoteCtx) error { return nil } -func newRun(ctx infra.DnoteCtx) core.RunEFunc { +func newRun(ctx context.DnoteCtx) infra.RunEFunc { return func(cmd *cobra.Command, args []string) error { err := Do(ctx) if err == ErrNotLoggedIn { diff --git a/cli/cmd/ls/ls.go b/pkg/cli/cmd/ls/ls.go similarity index 92% rename from cli/cmd/ls/ls.go rename to pkg/cli/cmd/ls/ls.go index 291d9229..8b07dd4b 100644 --- a/cli/cmd/ls/ls.go +++ b/pkg/cli/cmd/ls/ls.go @@ -23,9 +23,9 @@ import ( "fmt" "strings" - "github.com/dnote/dnote/cli/core" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/infra" + "github.com/dnote/dnote/pkg/cli/log" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -52,7 +52,7 @@ func preRun(cmd *cobra.Command, args []string) error { } // NewCmd returns a new ls command -func NewCmd(ctx infra.DnoteCtx) *cobra.Command { +func NewCmd(ctx context.DnoteCtx) *cobra.Command { cmd := &cobra.Command{ Use: "ls ", Aliases: []string{"l", "notes"}, @@ -67,7 +67,7 @@ func NewCmd(ctx infra.DnoteCtx) *cobra.Command { } // NewRun returns a new run function for ls -func NewRun(ctx infra.DnoteCtx, nameOnly bool) core.RunEFunc { +func NewRun(ctx context.DnoteCtx, nameOnly bool) infra.RunEFunc { return func(cmd *cobra.Command, args []string) error { if len(args) == 0 { if err := printBooks(ctx, nameOnly); err != nil { @@ -133,7 +133,7 @@ func printBookLine(info bookInfo, nameOnly bool) { } } -func printBooks(ctx infra.DnoteCtx, nameOnly bool) error { +func printBooks(ctx context.DnoteCtx, nameOnly bool) error { db := ctx.DB rows, err := db.Query(`SELECT books.label, count(notes.uuid) note_count @@ -165,7 +165,7 @@ func printBooks(ctx infra.DnoteCtx, nameOnly bool) error { return nil } -func printNotes(ctx infra.DnoteCtx, bookName string) error { +func printNotes(ctx context.DnoteCtx, bookName string) error { db := ctx.DB var bookUUID string diff --git a/cli/cmd/remove/remove.go b/pkg/cli/cmd/remove/remove.go similarity index 80% rename from cli/cmd/remove/remove.go rename to pkg/cli/cmd/remove/remove.go index 7e6f9d0b..ef98c9dc 100644 --- a/cli/cmd/remove/remove.go +++ b/pkg/cli/cmd/remove/remove.go @@ -21,10 +21,13 @@ package remove import ( "fmt" - "github.com/dnote/dnote/cli/core" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" - "github.com/dnote/dnote/cli/utils" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/infra" + "github.com/dnote/dnote/pkg/cli/log" + "github.com/dnote/dnote/pkg/cli/output" + "github.com/dnote/dnote/pkg/cli/ui" + "github.com/dnote/dnote/pkg/cli/utils" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -39,7 +42,7 @@ var example = ` dnote delete -b js` // NewCmd returns a new remove command -func NewCmd(ctx infra.DnoteCtx) *cobra.Command { +func NewCmd(ctx context.DnoteCtx) *cobra.Command { cmd := &cobra.Command{ Use: "remove", Short: "Remove a note or a book", @@ -54,7 +57,7 @@ func NewCmd(ctx infra.DnoteCtx) *cobra.Command { return cmd } -func newRun(ctx infra.DnoteCtx) core.RunEFunc { +func newRun(ctx context.DnoteCtx) infra.RunEFunc { return func(cmd *cobra.Command, args []string) error { if targetBookName != "" { if err := removeBook(ctx, targetBookName); err != nil { @@ -83,17 +86,17 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { } } -func removeNote(ctx infra.DnoteCtx, noteRowID string) error { +func removeNote(ctx context.DnoteCtx, noteRowID string) error { db := ctx.DB - noteInfo, err := core.GetNoteInfo(ctx, noteRowID) + noteInfo, err := database.GetNoteInfo(db, noteRowID) if err != nil { return err } - core.PrintNoteInfo(noteInfo) + output.NoteInfo(noteInfo) - ok, err := utils.AskConfirmation("remove this note?", false) + ok, err := ui.Confirm("remove this note?", false) if err != nil { return errors.Wrap(err, "getting confirmation") } @@ -117,15 +120,15 @@ func removeNote(ctx infra.DnoteCtx, noteRowID string) error { return nil } -func removeBook(ctx infra.DnoteCtx, bookLabel string) error { +func removeBook(ctx context.DnoteCtx, bookLabel string) error { db := ctx.DB - bookUUID, err := core.GetBookUUID(ctx, bookLabel) + bookUUID, err := database.GetBookUUID(db, bookLabel) if err != nil { return errors.Wrap(err, "finding book uuid") } - ok, err := utils.AskConfirmation(fmt.Sprintf("delete book '%s' and all its notes?", bookLabel), false) + ok, err := ui.Confirm(fmt.Sprintf("delete book '%s' and all its notes?", bookLabel), false) if err != nil { return errors.Wrap(err, "getting confirmation") } diff --git a/cli/cmd/root/root.go b/pkg/cli/cmd/root/root.go similarity index 58% rename from cli/cmd/root/root.go rename to pkg/cli/cmd/root/root.go index 3da8e7e0..681418d2 100644 --- a/cli/cmd/root/root.go +++ b/pkg/cli/cmd/root/root.go @@ -19,10 +19,11 @@ package root import ( - "github.com/dnote/dnote/cli/core" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/migrate" - "github.com/pkg/errors" + "github.com/dnote/dnote/pkg/cli/context" + // "github.com/dnote/dnote/pkg/cli/core" + // "github.com/dnote/dnote/pkg/cli/infra" + // "github.com/dnote/dnote/pkg/cli/migrate" + // "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -44,24 +45,24 @@ func Execute() error { } // Prepare initializes necessary files -func Prepare(ctx infra.DnoteCtx) error { - if err := core.InitFiles(ctx); err != nil { - return errors.Wrap(err, "initializing files") - } - - if err := infra.InitDB(ctx); err != nil { - return errors.Wrap(err, "initializing database") - } - if err := core.InitSystem(ctx); err != nil { - return errors.Wrap(err, "initializing system data") - } - - if err := migrate.Legacy(ctx); err != nil { - return errors.Wrap(err, "running legacy migration") - } - if err := migrate.Run(ctx, migrate.LocalSequence, migrate.LocalMode); err != nil { - return errors.Wrap(err, "running migration") - } +func Prepare(ctx context.DnoteCtx) error { + // if err := core.InitFiles(ctx); err != nil { + // return errors.Wrap(err, "initializing files") + // } + // + // if err := infra.InitDB(ctx); err != nil { + // return errors.Wrap(err, "initializing database") + // } + // if err := core.InitSystem(ctx); err != nil { + // return errors.Wrap(err, "initializing system data") + // } + // + // if err := migrate.Legacy(ctx); err != nil { + // return errors.Wrap(err, "running legacy migration") + // } + // if err := migrate.Run(ctx, migrate.LocalSequence, migrate.LocalMode); err != nil { + // return errors.Wrap(err, "running migration") + // } return nil } diff --git a/cli/cmd/sync/merge.go b/pkg/cli/cmd/sync/merge.go similarity index 89% rename from cli/cmd/sync/merge.go rename to pkg/cli/cmd/sync/merge.go index f558d14c..e7a6ba58 100644 --- a/cli/cmd/sync/merge.go +++ b/pkg/cli/cmd/sync/merge.go @@ -23,11 +23,10 @@ import ( "fmt" "strings" - "github.com/dnote/dnote/cli/client" - "github.com/dnote/dnote/cli/core" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/utils" - "github.com/dnote/dnote/cli/utils/diff" + "github.com/dnote/dnote/pkg/cli/client" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/utils" + "github.com/dnote/dnote/pkg/cli/utils/diff" "github.com/pkg/errors" ) @@ -102,7 +101,7 @@ func maxInt64(a, b int64) int64 { return b } -func reportBookConflict(tx *infra.DB, body, localBookUUID, serverBookUUID string) (string, error) { +func reportBookConflict(tx *database.DB, body, localBookUUID, serverBookUUID string) (string, error) { var builder strings.Builder var localBookName, serverBookName string @@ -123,14 +122,14 @@ func reportBookConflict(tx *infra.DB, body, localBookUUID, serverBookUUID string return builder.String(), nil } -func getConflictsBookUUID(tx *infra.DB) (string, error) { +func getConflictsBookUUID(tx *database.DB) (string, error) { var ret string err := tx.QueryRow("SELECT uuid FROM books WHERE label = ?", "conflicts").Scan(&ret) if err == sql.ErrNoRows { // Create a conflicts book ret = utils.GenerateUUID() - b := core.NewBook(ret, "conflicts", 0, false, true) + b := database.NewBook(ret, "conflicts", 0, false, true) err = b.Insert(tx) if err != nil { tx.Rollback() @@ -152,7 +151,7 @@ type noteMergeReport struct { // mergeNoteFields performs a field-by-field merge between the local and the server copy. It returns a merge report // between the local and the server copy of the note. -func mergeNoteFields(tx *infra.DB, localNote core.Note, serverNote client.SyncFragNote) (*noteMergeReport, error) { +func mergeNoteFields(tx *database.DB, localNote database.Note, serverNote client.SyncFragNote) (*noteMergeReport, error) { if !localNote.Dirty { return ¬eMergeReport{ body: serverNote.Body, diff --git a/cli/cmd/sync/merge_test.go b/pkg/cli/cmd/sync/merge_test.go similarity index 94% rename from cli/cmd/sync/merge_test.go rename to pkg/cli/cmd/sync/merge_test.go index 499c3a90..b9a8d713 100644 --- a/cli/cmd/sync/merge_test.go +++ b/pkg/cli/cmd/sync/merge_test.go @@ -22,7 +22,7 @@ import ( "fmt" "testing" - "github.com/dnote/dnote/cli/testutils" + "github.com/dnote/dnote/pkg/assert" ) func TestReportConflict(t *testing.T) { @@ -128,7 +128,7 @@ fuuz result := reportBodyConflict(tc.local, tc.server) t.Run(fmt.Sprintf("test case %d", idx), func(t *testing.T) { - testutils.AssertDeepEqual(t, result, tc.expected, "result mismatch") + assert.DeepEqual(t, result, tc.expected, "result mismatch") }) } } diff --git a/cli/cmd/sync/sync.go b/pkg/cli/cmd/sync/sync.go similarity index 88% rename from cli/cmd/sync/sync.go rename to pkg/cli/cmd/sync/sync.go index 4eb1cb32..e7877726 100644 --- a/cli/cmd/sync/sync.go +++ b/pkg/cli/cmd/sync/sync.go @@ -22,12 +22,15 @@ import ( "database/sql" "fmt" - "github.com/dnote/dnote/cli/client" - "github.com/dnote/dnote/cli/core" - "github.com/dnote/dnote/cli/crypt" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" - "github.com/dnote/dnote/cli/migrate" + "github.com/dnote/dnote/pkg/cli/client" + "github.com/dnote/dnote/pkg/cli/consts" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/crypt" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/infra" + "github.com/dnote/dnote/pkg/cli/log" + "github.com/dnote/dnote/pkg/cli/migrate" + "github.com/dnote/dnote/pkg/cli/upgrade" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -43,7 +46,7 @@ var example = ` var isFullSync bool // NewCmd returns a new sync command -func NewCmd(ctx infra.DnoteCtx) *cobra.Command { +func NewCmd(ctx context.DnoteCtx) *cobra.Command { cmd := &cobra.Command{ Use: "sync", Aliases: []string{"s"}, @@ -58,20 +61,20 @@ func NewCmd(ctx infra.DnoteCtx) *cobra.Command { return cmd } -func getLastSyncAt(tx *infra.DB) (int, error) { +func getLastSyncAt(tx *database.DB) (int, error) { var ret int - if err := core.GetSystem(tx, infra.SystemLastSyncAt, &ret); err != nil { + if err := database.GetSystem(tx, consts.SystemLastSyncAt, &ret); err != nil { return ret, errors.Wrap(err, "querying last sync time") } return ret, nil } -func getLastMaxUSN(tx *infra.DB) (int, error) { +func getLastMaxUSN(tx *database.DB) (int, error) { var ret int - if err := core.GetSystem(tx, infra.SystemLastMaxUSN, &ret); err != nil { + if err := database.GetSystem(tx, consts.SystemLastMaxUSN, &ret); err != nil { return ret, errors.Wrap(err, "querying last user max_usn") } @@ -152,7 +155,7 @@ func processFragments(fragments []client.SyncFragment, cipherKey []byte) (syncLi // getSyncList gets a list of all sync fragments after the specified usn // and aggregates them into a syncList data structure -func getSyncList(ctx infra.DnoteCtx, afterUSN int) (syncList, error) { +func getSyncList(ctx context.DnoteCtx, afterUSN int) (syncList, error) { fragments, err := getSyncFragments(ctx, afterUSN) if err != nil { return syncList{}, errors.Wrap(err, "getting sync fragments") @@ -168,7 +171,7 @@ func getSyncList(ctx infra.DnoteCtx, afterUSN int) (syncList, error) { // getSyncFragments repeatedly gets all sync fragments after the specified usn until there is no more new data // remaining and returns the buffered list -func getSyncFragments(ctx infra.DnoteCtx, afterUSN int) ([]client.SyncFragment, error) { +func getSyncFragments(ctx context.DnoteCtx, afterUSN int) ([]client.SyncFragment, error) { var buf []client.SyncFragment nextAfterUSN := afterUSN @@ -197,7 +200,7 @@ func getSyncFragments(ctx infra.DnoteCtx, afterUSN int) ([]client.SyncFragment, // resolveLabel resolves a book label conflict by repeatedly appending an increasing integer // to the label until it finds a unique label. It returns the first non-conflicting label. -func resolveLabel(tx *infra.DB, label string) (string, error) { +func resolveLabel(tx *database.DB, label string) (string, error) { var ret string for i := 2; ; i++ { @@ -218,7 +221,7 @@ func resolveLabel(tx *infra.DB, label string) (string, error) { // mergeBook inserts or updates the given book in the local database. // If a book with a duplicate label exists locally, it renames the duplicate by appending a number. -func mergeBook(tx *infra.DB, b client.SyncFragBook, mode int) error { +func mergeBook(tx *database.DB, b client.SyncFragBook, mode int) error { var count int if err := tx.QueryRow("SELECT count(*) FROM books WHERE label = ?", b.Label).Scan(&count); err != nil { return errors.Wrapf(err, "checking for books with a duplicate label %s", b.Label) @@ -237,7 +240,7 @@ func mergeBook(tx *infra.DB, b client.SyncFragBook, mode int) error { } if mode == modeInsert { - book := core.NewBook(b.UUID, b.Label, b.USN, false, false) + book := database.NewBook(b.UUID, b.Label, b.USN, false, false) if err := book.Insert(tx); err != nil { return errors.Wrapf(err, "inserting note with uuid %s", b.UUID) } @@ -252,7 +255,7 @@ func mergeBook(tx *infra.DB, b client.SyncFragBook, mode int) error { return nil } -func stepSyncBook(tx *infra.DB, b client.SyncFragBook) error { +func stepSyncBook(tx *database.DB, b client.SyncFragBook) error { var localUSN int var dirty bool err := tx.QueryRow("SELECT usn, dirty FROM books WHERE uuid = ?", b.UUID).Scan(&localUSN, &dirty) @@ -276,7 +279,7 @@ func stepSyncBook(tx *infra.DB, b client.SyncFragBook) error { return nil } -func mergeNote(tx *infra.DB, serverNote client.SyncFragNote, localNote core.Note) error { +func mergeNote(tx *database.DB, serverNote client.SyncFragNote, localNote database.Note) error { var bookDeleted bool err := tx.QueryRow("SELECT deleted FROM books WHERE uuid = ?", localNote.BookUUID).Scan(&bookDeleted) if err != nil { @@ -311,8 +314,8 @@ func mergeNote(tx *infra.DB, serverNote client.SyncFragNote, localNote core.Note return nil } -func stepSyncNote(tx *infra.DB, n client.SyncFragNote) error { - var localNote core.Note +func stepSyncNote(tx *database.DB, n client.SyncFragNote) error { + var localNote database.Note err := tx.QueryRow("SELECT body, usn, book_uuid, dirty, deleted FROM notes WHERE uuid = ?", n.UUID). Scan(&localNote.Body, &localNote.USN, &localNote.BookUUID, &localNote.Dirty, &localNote.Deleted) if err != nil && err != sql.ErrNoRows { @@ -321,7 +324,7 @@ func stepSyncNote(tx *infra.DB, n client.SyncFragNote) error { // if note exists in the server and does not exist in the client, insert the note. if err == sql.ErrNoRows { - note := core.NewNote(n.UUID, n.BookUUID, n.Body, n.AddedOn, n.EditedOn, n.USN, n.Public, n.Deleted, false) + note := database.NewNote(n.UUID, n.BookUUID, n.Body, n.AddedOn, n.EditedOn, n.USN, n.Public, n.Deleted, false) if err := note.Insert(tx); err != nil { return errors.Wrapf(err, "inserting note with uuid %s", n.UUID) @@ -335,8 +338,8 @@ func stepSyncNote(tx *infra.DB, n client.SyncFragNote) error { return nil } -func fullSyncNote(tx *infra.DB, n client.SyncFragNote) error { - var localNote core.Note +func fullSyncNote(tx *database.DB, n client.SyncFragNote) error { + var localNote database.Note err := tx.QueryRow("SELECT body, usn, book_uuid, dirty, deleted FROM notes WHERE uuid = ?", n.UUID). Scan(&localNote.Body, &localNote.USN, &localNote.BookUUID, &localNote.Dirty, &localNote.Deleted) if err != nil && err != sql.ErrNoRows { @@ -345,7 +348,7 @@ func fullSyncNote(tx *infra.DB, n client.SyncFragNote) error { // if note exists in the server and does not exist in the client, insert the note. if err == sql.ErrNoRows { - note := core.NewNote(n.UUID, n.BookUUID, n.Body, n.AddedOn, n.EditedOn, n.USN, n.Public, n.Deleted, false) + note := database.NewNote(n.UUID, n.BookUUID, n.Body, n.AddedOn, n.EditedOn, n.USN, n.Public, n.Deleted, false) if err := note.Insert(tx); err != nil { return errors.Wrapf(err, "inserting note with uuid %s", n.UUID) @@ -359,7 +362,7 @@ func fullSyncNote(tx *infra.DB, n client.SyncFragNote) error { return nil } -func syncDeleteNote(tx *infra.DB, noteUUID string) error { +func syncDeleteNote(tx *database.DB, noteUUID string) error { var localUSN int var dirty bool err := tx.QueryRow("SELECT usn, dirty FROM notes WHERE uuid = ?", noteUUID).Scan(&localUSN, &dirty) @@ -384,7 +387,7 @@ func syncDeleteNote(tx *infra.DB, noteUUID string) error { } // checkNotesPristine checks that none of the notes in the given book are dirty -func checkNotesPristine(tx *infra.DB, bookUUID string) (bool, error) { +func checkNotesPristine(tx *database.DB, bookUUID string) (bool, error) { var count int if err := tx.QueryRow("SELECT count(*) FROM notes WHERE book_uuid = ? AND dirty = ?", bookUUID, true).Scan(&count); err != nil { return false, errors.Wrapf(err, "counting notes that are dirty in book %s", bookUUID) @@ -397,7 +400,7 @@ func checkNotesPristine(tx *infra.DB, bookUUID string) (bool, error) { return true, nil } -func syncDeleteBook(tx *infra.DB, bookUUID string) error { +func syncDeleteBook(tx *database.DB, bookUUID string) error { var localUSN int var dirty bool err := tx.QueryRow("SELECT usn, dirty FROM books WHERE uuid = ?", bookUUID).Scan(&localUSN, &dirty) @@ -443,7 +446,7 @@ func syncDeleteBook(tx *infra.DB, bookUUID string) error { return nil } -func fullSyncBook(tx *infra.DB, b client.SyncFragBook) error { +func fullSyncBook(tx *database.DB, b client.SyncFragBook) error { var localUSN int var dirty bool err := tx.QueryRow("SELECT usn, dirty FROM books WHERE uuid = ?", b.UUID).Scan(&localUSN, &dirty) @@ -495,7 +498,7 @@ func checkBookInList(uuid string, list *syncList) bool { // judging by the full list of resources in the server. Concretely, the only acceptable // situation in which a local note is not present in the server is if it is new and has not been // uploaded (i.e. dirty and usn is 0). Otherwise, it is a result of some kind of error and should be cleaned. -func cleanLocalNotes(tx *infra.DB, fullList *syncList) error { +func cleanLocalNotes(tx *database.DB, fullList *syncList) error { rows, err := tx.Query("SELECT uuid, usn, dirty FROM notes") if err != nil { return errors.Wrap(err, "getting local notes") @@ -503,7 +506,7 @@ func cleanLocalNotes(tx *infra.DB, fullList *syncList) error { defer rows.Close() for rows.Next() { - var note core.Note + var note database.Note if err := rows.Scan(¬e.UUID, ¬e.USN, ¬e.Dirty); err != nil { return errors.Wrap(err, "scanning a row for local note") } @@ -521,7 +524,7 @@ func cleanLocalNotes(tx *infra.DB, fullList *syncList) error { } // cleanLocalBooks deletes from the local database any books that are in invalid state -func cleanLocalBooks(tx *infra.DB, fullList *syncList) error { +func cleanLocalBooks(tx *database.DB, fullList *syncList) error { rows, err := tx.Query("SELECT uuid, usn, dirty FROM books") if err != nil { return errors.Wrap(err, "getting local books") @@ -529,7 +532,7 @@ func cleanLocalBooks(tx *infra.DB, fullList *syncList) error { defer rows.Close() for rows.Next() { - var book core.Book + var book database.Book if err := rows.Scan(&book.UUID, &book.USN, &book.Dirty); err != nil { return errors.Wrap(err, "scanning a row for local book") } @@ -546,7 +549,7 @@ func cleanLocalBooks(tx *infra.DB, fullList *syncList) error { return nil } -func fullSync(ctx infra.DnoteCtx, tx *infra.DB) error { +func fullSync(ctx context.DnoteCtx, tx *database.DB) error { log.Debug("performing a full sync\n") log.Info("resolving delta.") @@ -597,7 +600,7 @@ func fullSync(ctx infra.DnoteCtx, tx *infra.DB) error { return nil } -func stepSync(ctx infra.DnoteCtx, tx *infra.DB, afterUSN int) error { +func stepSync(ctx context.DnoteCtx, tx *database.DB, afterUSN int) error { log.Debug("performing a step sync\n") log.Info("resolving delta.") @@ -641,7 +644,7 @@ func stepSync(ctx infra.DnoteCtx, tx *infra.DB, afterUSN int) error { return nil } -func sendBooks(ctx infra.DnoteCtx, tx *infra.DB) (bool, error) { +func sendBooks(ctx context.DnoteCtx, tx *database.DB) (bool, error) { isBehind := false rows, err := tx.Query("SELECT uuid, label, usn, deleted FROM books WHERE dirty") @@ -651,7 +654,7 @@ func sendBooks(ctx infra.DnoteCtx, tx *infra.DB) (bool, error) { defer rows.Close() for rows.Next() { - var book core.Book + var book database.Book if err = rows.Scan(&book.UUID, &book.Label, &book.USN, &book.Deleted); err != nil { return isBehind, errors.Wrap(err, "scanning a syncable book") @@ -745,7 +748,7 @@ func sendBooks(ctx infra.DnoteCtx, tx *infra.DB) (bool, error) { return isBehind, nil } -func sendNotes(ctx infra.DnoteCtx, tx *infra.DB) (bool, error) { +func sendNotes(ctx context.DnoteCtx, tx *database.DB) (bool, error) { isBehind := false rows, err := tx.Query("SELECT uuid, book_uuid, body, public, deleted, usn, added_on FROM notes WHERE dirty") @@ -755,7 +758,7 @@ func sendNotes(ctx infra.DnoteCtx, tx *infra.DB) (bool, error) { defer rows.Close() for rows.Next() { - var note core.Note + var note database.Note if err = rows.Scan(¬e.UUID, ¬e.BookUUID, ¬e.Body, ¬e.Public, ¬e.Deleted, ¬e.USN, ¬e.AddedOn); err != nil { return isBehind, errors.Wrap(err, "scanning a syncable note") @@ -845,7 +848,7 @@ func sendNotes(ctx infra.DnoteCtx, tx *infra.DB) (bool, error) { return isBehind, nil } -func sendChanges(ctx infra.DnoteCtx, tx *infra.DB) (bool, error) { +func sendChanges(ctx context.DnoteCtx, tx *database.DB) (bool, error) { log.Info("sending changes.") var delta int @@ -870,23 +873,23 @@ func sendChanges(ctx infra.DnoteCtx, tx *infra.DB) (bool, error) { return isBehind, nil } -func updateLastMaxUSN(tx *infra.DB, val int) error { - if err := core.UpdateSystem(tx, infra.SystemLastMaxUSN, val); err != nil { - return errors.Wrapf(err, "updating %s", infra.SystemLastMaxUSN) +func updateLastMaxUSN(tx *database.DB, val int) error { + if err := database.UpdateSystem(tx, consts.SystemLastMaxUSN, val); err != nil { + return errors.Wrapf(err, "updating %s", consts.SystemLastMaxUSN) } return nil } -func updateLastSyncAt(tx *infra.DB, val int64) error { - if err := core.UpdateSystem(tx, infra.SystemLastSyncAt, val); err != nil { - return errors.Wrapf(err, "updating %s", infra.SystemLastSyncAt) +func updateLastSyncAt(tx *database.DB, val int64) error { + if err := database.UpdateSystem(tx, consts.SystemLastSyncAt, val); err != nil { + return errors.Wrapf(err, "updating %s", consts.SystemLastSyncAt) } return nil } -func saveSyncState(tx *infra.DB, serverTime int64, serverMaxUSN int) error { +func saveSyncState(tx *database.DB, serverTime int64, serverMaxUSN int) error { if err := updateLastMaxUSN(tx, serverMaxUSN); err != nil { return errors.Wrap(err, "updating last max usn") } @@ -897,7 +900,7 @@ func saveSyncState(tx *infra.DB, serverTime int64, serverMaxUSN int) error { return nil } -func newRun(ctx infra.DnoteCtx) core.RunEFunc { +func newRun(ctx context.DnoteCtx) infra.RunEFunc { return func(cmd *cobra.Command, args []string) error { if ctx.SessionKey == "" || ctx.CipherKey == nil { return errors.New("not logged in") @@ -971,7 +974,7 @@ func newRun(ctx infra.DnoteCtx) core.RunEFunc { log.Success("success\n") - if err := core.CheckUpdate(ctx); err != nil { + if err := upgrade.Check(ctx); err != nil { log.Error(errors.Wrap(err, "automatically checking updates").Error()) } diff --git a/cli/cmd/sync/sync_test.go b/pkg/cli/cmd/sync/sync_test.go similarity index 50% rename from cli/cmd/sync/sync_test.go rename to pkg/cli/cmd/sync/sync_test.go index 00ee936c..8959c30a 100644 --- a/cli/cmd/sync/sync_test.go +++ b/pkg/cli/cmd/sync/sync_test.go @@ -27,22 +27,21 @@ import ( "strings" "testing" - "github.com/dnote/dnote/cli/client" - "github.com/dnote/dnote/cli/core" - "github.com/dnote/dnote/cli/crypt" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/testutils" - "github.com/dnote/dnote/cli/utils" + "github.com/dnote/dnote/pkg/assert" + "github.com/dnote/dnote/pkg/cli/client" + "github.com/dnote/dnote/pkg/cli/consts" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/crypt" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/testutils" + "github.com/dnote/dnote/pkg/cli/utils" "github.com/pkg/errors" ) var cipherKey = []byte("AES256Key-32Characters1234567890") +var dbPath = "../../tmp/.dnote.db" func TestProcessFragments(t *testing.T) { - // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - fragments := []client.SyncFragment{ client.SyncFragment{ FragMaxUSN: 10, @@ -107,16 +106,14 @@ func TestProcessFragments(t *testing.T) { } // test - testutils.AssertDeepEqual(t, sl, expected, "syncList mismatch") + assert.DeepEqual(t, sl, expected, "syncList mismatch") } func TestGetLastSyncAt(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - testutils.MustExec(t, "setting up last_sync_at", db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemLastSyncAt, 1541108743) + db := database.InitTestDB(t, "../../tmp/.dnote", nil) + defer database.CloseTestDB(t, db) + database.MustExec(t, "setting up last_sync_at", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemLastSyncAt, 1541108743) // exec tx, err := db.Begin() @@ -132,16 +129,14 @@ func TestGetLastSyncAt(t *testing.T) { tx.Commit() // test - testutils.AssertEqual(t, got, 1541108743, "last_sync_at mismatch") + assert.Equal(t, got, 1541108743, "last_sync_at mismatch") } func TestGetLastMaxUSN(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - testutils.MustExec(t, "setting up last_max_usn", db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemLastMaxUSN, 20001) + db := database.InitTestDB(t, "../../tmp/.dnote", nil) + defer database.CloseTestDB(t, db) + database.MustExec(t, "setting up last_max_usn", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemLastMaxUSN, 20001) // exec tx, err := db.Begin() @@ -157,7 +152,7 @@ func TestGetLastMaxUSN(t *testing.T) { tx.Commit() // test - testutils.AssertEqual(t, got, 20001, "last_max_usn mismatch") + assert.Equal(t, got, 20001, "last_max_usn mismatch") } func TestResolveLabel(t *testing.T) { @@ -186,17 +181,15 @@ func TestResolveLabel(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, "../../tmp/.dnote", nil) + defer database.CloseTestDB(t, db) - db := ctx.DB - - testutils.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", "b1-uuid", "js") - testutils.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", "b2-uuid", "css_2") - testutils.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", "b3-uuid", "linux_(1)") - testutils.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", "b4-uuid", "linux_2") - testutils.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", "b5-uuid", "linux_3") - testutils.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", "b6-uuid", "cool_ideas") + database.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", "b1-uuid", "js") + database.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", "b2-uuid", "css_2") + database.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", "b3-uuid", "linux_(1)") + database.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", "b4-uuid", "linux_2") + database.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", "b5-uuid", "linux_3") + database.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", "b6-uuid", "cool_ideas") // execute tx, err := db.Begin() @@ -210,7 +203,7 @@ func TestResolveLabel(t *testing.T) { } tx.Rollback() - testutils.AssertEqual(t, got, tc.expected, fmt.Sprintf("output mismatch for test case %d", idx)) + assert.Equal(t, got, tc.expected, fmt.Sprintf("output mismatch for test case %d", idx)) }() } } @@ -218,10 +211,8 @@ func TestResolveLabel(t *testing.T) { func TestSyncDeleteNote(t *testing.T) { t.Run("exists on server only", func(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) // execute tx, err := db.Begin() @@ -238,32 +229,30 @@ func TestSyncDeleteNote(t *testing.T) { // test var noteCount, bookCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 0, "note count mismatch") - testutils.AssertEqualf(t, bookCount, 0, "book count mismatch") + assert.Equalf(t, noteCount, 0, "note count mismatch") + assert.Equalf(t, bookCount, 0, "book count mismatch") }) t.Run("local copy is dirty", func(t *testing.T) { b1UUID := utils.GenerateUUID() // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) - db := ctx.DB + database.MustExec(t, "inserting b1 for test case %d", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") + database.MustExec(t, "inserting n1 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 10, "n1 body", 1541108743, false, true) + database.MustExec(t, "inserting n2 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n2-uuid", b1UUID, 11, "n2 body", 1541108743, false, true) - testutils.MustExec(t, "inserting b1 for test case %d", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") - testutils.MustExec(t, "inserting n1 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 10, "n1 body", 1541108743, false, true) - testutils.MustExec(t, "inserting n2 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n2-uuid", b1UUID, 11, "n2 body", 1541108743, false, true) - - var n1 core.Note - testutils.MustScan(t, "getting n1 for test case", + var n1 database.Note + database.MustScan(t, "getting n1 for test case", db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", "n1-uuid"), &n1.UUID, &n1.BookUUID, &n1.USN, &n1.AddedOn, &n1.EditedOn, &n1.Body, &n1.Deleted, &n1.Dirty) - var n2 core.Note - testutils.MustScan(t, "getting n2 for test case", + var n2 database.Note + database.MustScan(t, "getting n2 for test case", db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", "n2-uuid"), &n2.UUID, &n2.BookUUID, &n2.USN, &n2.AddedOn, &n2.EditedOn, &n2.Body, &n2.Deleted, &n2.Dirty) @@ -282,60 +271,58 @@ func TestSyncDeleteNote(t *testing.T) { // test var noteCount, bookCount int - testutils.MustScan(t, "counting notes for test case", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books for test case", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes for test case", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books for test case", db.QueryRow("SELECT count(*) FROM books"), &bookCount) // do not delete note if local copy is dirty - testutils.AssertEqualf(t, noteCount, 2, "note count mismatch for test case") - testutils.AssertEqualf(t, bookCount, 1, "book count mismatch for test case") + assert.Equalf(t, noteCount, 2, "note count mismatch for test case") + assert.Equalf(t, bookCount, 1, "book count mismatch for test case") - var n1Record core.Note - testutils.MustScan(t, "getting n1 for test case", + var n1Record database.Note + database.MustScan(t, "getting n1 for test case", db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", n1.UUID), &n1Record.UUID, &n1Record.BookUUID, &n1Record.USN, &n1Record.AddedOn, &n1Record.EditedOn, &n1Record.Body, &n1Record.Deleted, &n1Record.Dirty) - var n2Record core.Note - testutils.MustScan(t, "getting n2 for test case", + var n2Record database.Note + database.MustScan(t, "getting n2 for test case", db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", n2.UUID), &n2Record.UUID, &n2Record.BookUUID, &n2Record.USN, &n2Record.AddedOn, &n2Record.EditedOn, &n2Record.Body, &n2Record.Deleted, &n2Record.Dirty) - testutils.AssertEqual(t, n1Record.UUID, n1.UUID, "n1 UUID mismatch for test case") - testutils.AssertEqual(t, n1Record.BookUUID, n1.BookUUID, "n1 BookUUID mismatch for test case") - testutils.AssertEqual(t, n1Record.USN, n1.USN, "n1 USN mismatch for test case") - testutils.AssertEqual(t, n1Record.AddedOn, n1.AddedOn, "n1 AddedOn mismatch for test case") - testutils.AssertEqual(t, n1Record.EditedOn, n1.EditedOn, "n1 EditedOn mismatch for test case") - testutils.AssertEqual(t, n1Record.Body, n1.Body, "n1 Body mismatch for test case") - testutils.AssertEqual(t, n1Record.Deleted, n1.Deleted, "n1 Deleted mismatch for test case") - testutils.AssertEqual(t, n1Record.Dirty, n1.Dirty, "n1 Dirty mismatch for test case") + assert.Equal(t, n1Record.UUID, n1.UUID, "n1 UUID mismatch for test case") + assert.Equal(t, n1Record.BookUUID, n1.BookUUID, "n1 BookUUID mismatch for test case") + assert.Equal(t, n1Record.USN, n1.USN, "n1 USN mismatch for test case") + assert.Equal(t, n1Record.AddedOn, n1.AddedOn, "n1 AddedOn mismatch for test case") + assert.Equal(t, n1Record.EditedOn, n1.EditedOn, "n1 EditedOn mismatch for test case") + assert.Equal(t, n1Record.Body, n1.Body, "n1 Body mismatch for test case") + assert.Equal(t, n1Record.Deleted, n1.Deleted, "n1 Deleted mismatch for test case") + assert.Equal(t, n1Record.Dirty, n1.Dirty, "n1 Dirty mismatch for test case") - testutils.AssertEqual(t, n2Record.UUID, n2.UUID, "n2 UUID mismatch for test case") - testutils.AssertEqual(t, n2Record.BookUUID, n2.BookUUID, "n2 BookUUID mismatch for test case") - testutils.AssertEqual(t, n2Record.USN, n2.USN, "n2 USN mismatch for test case") - testutils.AssertEqual(t, n2Record.AddedOn, n2.AddedOn, "n2 AddedOn mismatch for test case") - testutils.AssertEqual(t, n2Record.EditedOn, n2.EditedOn, "n2 EditedOn mismatch for test case") - testutils.AssertEqual(t, n2Record.Body, n2.Body, "n2 Body mismatch for test case") - testutils.AssertEqual(t, n2Record.Deleted, n2.Deleted, "n2 Deleted mismatch for test case") - testutils.AssertEqual(t, n2Record.Dirty, n2.Dirty, "n2 Dirty mismatch for test case") + assert.Equal(t, n2Record.UUID, n2.UUID, "n2 UUID mismatch for test case") + assert.Equal(t, n2Record.BookUUID, n2.BookUUID, "n2 BookUUID mismatch for test case") + assert.Equal(t, n2Record.USN, n2.USN, "n2 USN mismatch for test case") + assert.Equal(t, n2Record.AddedOn, n2.AddedOn, "n2 AddedOn mismatch for test case") + assert.Equal(t, n2Record.EditedOn, n2.EditedOn, "n2 EditedOn mismatch for test case") + assert.Equal(t, n2Record.Body, n2.Body, "n2 Body mismatch for test case") + assert.Equal(t, n2Record.Deleted, n2.Deleted, "n2 Deleted mismatch for test case") + assert.Equal(t, n2Record.Dirty, n2.Dirty, "n2 Dirty mismatch for test case") }) t.Run("local copy is not dirty", func(t *testing.T) { b1UUID := utils.GenerateUUID() // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) - db := ctx.DB + database.MustExec(t, "inserting b1 for test case %d", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") + database.MustExec(t, "inserting n1 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 10, "n1 body", 1541108743, false, false) + database.MustExec(t, "inserting n2 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n2-uuid", b1UUID, 11, "n2 body", 1541108743, false, false) - testutils.MustExec(t, "inserting b1 for test case %d", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") - testutils.MustExec(t, "inserting n1 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 10, "n1 body", 1541108743, false, false) - testutils.MustExec(t, "inserting n2 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n2-uuid", b1UUID, 11, "n2 body", 1541108743, false, false) - - var n1 core.Note - testutils.MustScan(t, "getting n1 for test case", + var n1 database.Note + database.MustScan(t, "getting n1 for test case", db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", "n1-uuid"), &n1.UUID, &n1.BookUUID, &n1.USN, &n1.AddedOn, &n1.EditedOn, &n1.Body, &n1.Deleted, &n1.Dirty) - var n2 core.Note - testutils.MustScan(t, "getting n2 for test case", + var n2 database.Note + database.MustScan(t, "getting n2 for test case", db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", "n2-uuid"), &n2.UUID, &n2.BookUUID, &n2.USN, &n2.AddedOn, &n2.EditedOn, &n2.Body, &n2.Deleted, &n2.Dirty) @@ -354,39 +341,37 @@ func TestSyncDeleteNote(t *testing.T) { // test var noteCount, bookCount int - testutils.MustScan(t, "counting notes for test case", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books for test case", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes for test case", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books for test case", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 1, "note count mismatch for test case") - testutils.AssertEqualf(t, bookCount, 1, "book count mismatch for test case") + assert.Equalf(t, noteCount, 1, "note count mismatch for test case") + assert.Equalf(t, bookCount, 1, "book count mismatch for test case") - var n2Record core.Note - testutils.MustScan(t, "getting n2 for test case", + var n2Record database.Note + database.MustScan(t, "getting n2 for test case", db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", n2.UUID), &n2Record.UUID, &n2Record.BookUUID, &n2Record.USN, &n2Record.AddedOn, &n2Record.EditedOn, &n2Record.Body, &n2Record.Deleted, &n2Record.Dirty) - testutils.AssertEqual(t, n2Record.UUID, n2.UUID, "n2 UUID mismatch for test case") - testutils.AssertEqual(t, n2Record.BookUUID, n2.BookUUID, "n2 BookUUID mismatch for test case") - testutils.AssertEqual(t, n2Record.USN, n2.USN, "n2 USN mismatch for test case") - testutils.AssertEqual(t, n2Record.AddedOn, n2.AddedOn, "n2 AddedOn mismatch for test case") - testutils.AssertEqual(t, n2Record.EditedOn, n2.EditedOn, "n2 EditedOn mismatch for test case") - testutils.AssertEqual(t, n2Record.Body, n2.Body, "n2 Body mismatch for test case") - testutils.AssertEqual(t, n2Record.Deleted, n2.Deleted, "n2 Deleted mismatch for test case") - testutils.AssertEqual(t, n2Record.Dirty, n2.Dirty, "n2 Dirty mismatch for test case") + assert.Equal(t, n2Record.UUID, n2.UUID, "n2 UUID mismatch for test case") + assert.Equal(t, n2Record.BookUUID, n2.BookUUID, "n2 BookUUID mismatch for test case") + assert.Equal(t, n2Record.USN, n2.USN, "n2 USN mismatch for test case") + assert.Equal(t, n2Record.AddedOn, n2.AddedOn, "n2 AddedOn mismatch for test case") + assert.Equal(t, n2Record.EditedOn, n2.EditedOn, "n2 EditedOn mismatch for test case") + assert.Equal(t, n2Record.Body, n2.Body, "n2 Body mismatch for test case") + assert.Equal(t, n2Record.Deleted, n2.Deleted, "n2 Deleted mismatch for test case") + assert.Equal(t, n2Record.Dirty, n2.Dirty, "n2 Dirty mismatch for test case") }) } func TestSyncDeleteBook(t *testing.T) { t.Run("exists on server only", func(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) + database.MustExec(t, "inserting b1 for test case %d", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", "b1-uuid", "b1-label") - db := ctx.DB - testutils.MustExec(t, "inserting b1 for test case %d", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", "b1-uuid", "b1-label") - - var b1 core.Book - testutils.MustScan(t, "getting b1 for test case", + var b1 database.Book + database.MustScan(t, "getting b1 for test case", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b1-uuid"), &b1.UUID, &b1.Label, &b1.USN, &b1.Dirty) @@ -405,41 +390,39 @@ func TestSyncDeleteBook(t *testing.T) { // test var noteCount, bookCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 0, "note count mismatch") - testutils.AssertEqualf(t, bookCount, 1, "book count mismatch") + assert.Equalf(t, noteCount, 0, "note count mismatch") + assert.Equalf(t, bookCount, 1, "book count mismatch") - var b1Record core.Book - testutils.MustScan(t, "getting b1 for test case", + var b1Record database.Book + database.MustScan(t, "getting b1 for test case", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b1-uuid"), &b1Record.UUID, &b1Record.Label, &b1Record.USN, &b1Record.Dirty) - testutils.AssertEqual(t, b1Record.UUID, b1.UUID, "b1 UUID mismatch for test case") - testutils.AssertEqual(t, b1Record.Label, b1.Label, "b1 Label mismatch for test case") - testutils.AssertEqual(t, b1Record.USN, b1.USN, "b1 USN mismatch for test case") - testutils.AssertEqual(t, b1Record.Dirty, b1.Dirty, "b1 Dirty mismatch for test case") + assert.Equal(t, b1Record.UUID, b1.UUID, "b1 UUID mismatch for test case") + assert.Equal(t, b1Record.Label, b1.Label, "b1 Label mismatch for test case") + assert.Equal(t, b1Record.USN, b1.USN, "b1 USN mismatch for test case") + assert.Equal(t, b1Record.Dirty, b1.Dirty, "b1 Dirty mismatch for test case") }) t.Run("local copy is dirty", func(t *testing.T) { b1UUID := utils.GenerateUUID() // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) - db := ctx.DB + database.MustExec(t, "inserting b1 for test case %d", db, "INSERT INTO books (uuid, label, usn, dirty) VALUES (?, ?, ?, ?)", b1UUID, "b1-label", 12, true) + database.MustExec(t, "inserting n1 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 10, "n1 body", 1541108743, false, true) - testutils.MustExec(t, "inserting b1 for test case %d", db, "INSERT INTO books (uuid, label, usn, dirty) VALUES (?, ?, ?, ?)", b1UUID, "b1-label", 12, true) - testutils.MustExec(t, "inserting n1 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 10, "n1 body", 1541108743, false, true) - - var b1 core.Book - testutils.MustScan(t, "getting b1 for test case", + var b1 database.Book + database.MustScan(t, "getting b1 for test case", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", b1UUID), &b1.UUID, &b1.Label, &b1.USN, &b1.Dirty) - var n1 core.Note - testutils.MustScan(t, "getting n1 for test case", + var n1 database.Note + database.MustScan(t, "getting n1 for test case", db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", "n1-uuid"), &n1.UUID, &n1.BookUUID, &n1.USN, &n1.AddedOn, &n1.EditedOn, &n1.Body, &n1.Deleted, &n1.Dirty) @@ -458,35 +441,35 @@ func TestSyncDeleteBook(t *testing.T) { // test var noteCount, bookCount int - testutils.MustScan(t, "counting notes for test case", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books for test case", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes for test case", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books for test case", db.QueryRow("SELECT count(*) FROM books"), &bookCount) // do not delete note if local copy is dirty - testutils.AssertEqualf(t, noteCount, 1, "note count mismatch for test case") - testutils.AssertEqualf(t, bookCount, 1, "book count mismatch for test case") + assert.Equalf(t, noteCount, 1, "note count mismatch for test case") + assert.Equalf(t, bookCount, 1, "book count mismatch for test case") - var b1Record core.Book - testutils.MustScan(t, "getting b1Record for test case", + var b1Record database.Book + database.MustScan(t, "getting b1Record for test case", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", b1UUID), &b1Record.UUID, &b1Record.Label, &b1Record.USN, &b1Record.Dirty) - var n1Record core.Note - testutils.MustScan(t, "getting n1 for test case", + var n1Record database.Note + database.MustScan(t, "getting n1 for test case", db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", n1.UUID), &n1Record.UUID, &n1Record.BookUUID, &n1Record.USN, &n1Record.AddedOn, &n1Record.EditedOn, &n1Record.Body, &n1Record.Deleted, &n1Record.Dirty) - testutils.AssertEqual(t, b1Record.UUID, b1.UUID, "b1 UUID mismatch for test case") - testutils.AssertEqual(t, b1Record.Label, b1.Label, "b1 Label mismatch for test case") - testutils.AssertEqual(t, b1Record.USN, b1.USN, "b1 USN mismatch for test case") - testutils.AssertEqual(t, b1Record.Dirty, b1.Dirty, "b1 Dirty mismatch for test case") + assert.Equal(t, b1Record.UUID, b1.UUID, "b1 UUID mismatch for test case") + assert.Equal(t, b1Record.Label, b1.Label, "b1 Label mismatch for test case") + assert.Equal(t, b1Record.USN, b1.USN, "b1 USN mismatch for test case") + assert.Equal(t, b1Record.Dirty, b1.Dirty, "b1 Dirty mismatch for test case") - testutils.AssertEqual(t, n1Record.UUID, n1.UUID, "n1 UUID mismatch for test case") - testutils.AssertEqual(t, n1Record.BookUUID, n1.BookUUID, "n1 BookUUID mismatch for test case") - testutils.AssertEqual(t, n1Record.USN, n1.USN, "n1 USN mismatch for test case") - testutils.AssertEqual(t, n1Record.AddedOn, n1.AddedOn, "n1 AddedOn mismatch for test case") - testutils.AssertEqual(t, n1Record.EditedOn, n1.EditedOn, "n1 EditedOn mismatch for test case") - testutils.AssertEqual(t, n1Record.Body, n1.Body, "n1 Body mismatch for test case") - testutils.AssertEqual(t, n1Record.Deleted, n1.Deleted, "n1 Deleted mismatch for test case") - testutils.AssertEqual(t, n1Record.Dirty, n1.Dirty, "n1 Dirty mismatch for test case") + assert.Equal(t, n1Record.UUID, n1.UUID, "n1 UUID mismatch for test case") + assert.Equal(t, n1Record.BookUUID, n1.BookUUID, "n1 BookUUID mismatch for test case") + assert.Equal(t, n1Record.USN, n1.USN, "n1 USN mismatch for test case") + assert.Equal(t, n1Record.AddedOn, n1.AddedOn, "n1 AddedOn mismatch for test case") + assert.Equal(t, n1Record.EditedOn, n1.EditedOn, "n1 EditedOn mismatch for test case") + assert.Equal(t, n1Record.Body, n1.Body, "n1 Body mismatch for test case") + assert.Equal(t, n1Record.Deleted, n1.Deleted, "n1 Deleted mismatch for test case") + assert.Equal(t, n1Record.Dirty, n1.Dirty, "n1 Dirty mismatch for test case") }) t.Run("local copy is not dirty", func(t *testing.T) { @@ -494,22 +477,20 @@ func TestSyncDeleteBook(t *testing.T) { b2UUID := utils.GenerateUUID() // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) - db := ctx.DB + database.MustExec(t, "inserting b1 for test case %d", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") + database.MustExec(t, "inserting n1 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 10, "n1 body", 1541108743, false, false) + database.MustExec(t, "inserting b2 for test case %d", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "b2-label") + database.MustExec(t, "inserting n2 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n2-uuid", b2UUID, 11, "n2 body", 1541108743, false, false) - testutils.MustExec(t, "inserting b1 for test case %d", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") - testutils.MustExec(t, "inserting n1 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 10, "n1 body", 1541108743, false, false) - testutils.MustExec(t, "inserting b2 for test case %d", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "b2-label") - testutils.MustExec(t, "inserting n2 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n2-uuid", b2UUID, 11, "n2 body", 1541108743, false, false) - - var b2 core.Book - testutils.MustScan(t, "getting b2 for test case", + var b2 database.Book + database.MustScan(t, "getting b2 for test case", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", b2UUID), &b2.UUID, &b2.Label, &b2.USN, &b2.Dirty) - var n2 core.Note - testutils.MustScan(t, "getting n2 for test case", + var n2 database.Note + database.MustScan(t, "getting n2 for test case", db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", "n2-uuid"), &n2.UUID, &n2.BookUUID, &n2.USN, &n2.AddedOn, &n2.EditedOn, &n2.Body, &n2.Deleted, &n2.Dirty) @@ -528,47 +509,45 @@ func TestSyncDeleteBook(t *testing.T) { // test var noteCount, bookCount int - testutils.MustScan(t, "counting notes for test case", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books for test case", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes for test case", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books for test case", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 1, "note count mismatch for test case") - testutils.AssertEqualf(t, bookCount, 1, "book count mismatch for test case") + assert.Equalf(t, noteCount, 1, "note count mismatch for test case") + assert.Equalf(t, bookCount, 1, "book count mismatch for test case") - var b2Record core.Book - testutils.MustScan(t, "getting b2 for test case", + var b2Record database.Book + database.MustScan(t, "getting b2 for test case", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", b2UUID), &b2Record.UUID, &b2Record.Label, &b2Record.USN, &b2Record.Dirty) - var n2Record core.Note - testutils.MustScan(t, "getting n2 for test case", + var n2Record database.Note + database.MustScan(t, "getting n2 for test case", db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", n2.UUID), &n2Record.UUID, &n2Record.BookUUID, &n2Record.USN, &n2Record.AddedOn, &n2Record.EditedOn, &n2Record.Body, &n2Record.Deleted, &n2Record.Dirty) - testutils.AssertEqual(t, b2Record.UUID, b2.UUID, "b2 UUID mismatch for test case") - testutils.AssertEqual(t, b2Record.Label, b2.Label, "b2 Label mismatch for test case") - testutils.AssertEqual(t, b2Record.USN, b2.USN, "b2 USN mismatch for test case") - testutils.AssertEqual(t, b2Record.Dirty, b2.Dirty, "b2 Dirty mismatch for test case") + assert.Equal(t, b2Record.UUID, b2.UUID, "b2 UUID mismatch for test case") + assert.Equal(t, b2Record.Label, b2.Label, "b2 Label mismatch for test case") + assert.Equal(t, b2Record.USN, b2.USN, "b2 USN mismatch for test case") + assert.Equal(t, b2Record.Dirty, b2.Dirty, "b2 Dirty mismatch for test case") - testutils.AssertEqual(t, n2Record.UUID, n2.UUID, "n2 UUID mismatch for test case") - testutils.AssertEqual(t, n2Record.BookUUID, n2.BookUUID, "n2 BookUUID mismatch for test case") - testutils.AssertEqual(t, n2Record.USN, n2.USN, "n2 USN mismatch for test case") - testutils.AssertEqual(t, n2Record.AddedOn, n2.AddedOn, "n2 AddedOn mismatch for test case") - testutils.AssertEqual(t, n2Record.EditedOn, n2.EditedOn, "n2 EditedOn mismatch for test case") - testutils.AssertEqual(t, n2Record.Body, n2.Body, "n2 Body mismatch for test case") - testutils.AssertEqual(t, n2Record.Deleted, n2.Deleted, "n2 Deleted mismatch for test case") - testutils.AssertEqual(t, n2Record.Dirty, n2.Dirty, "n2 Dirty mismatch for test case") + assert.Equal(t, n2Record.UUID, n2.UUID, "n2 UUID mismatch for test case") + assert.Equal(t, n2Record.BookUUID, n2.BookUUID, "n2 BookUUID mismatch for test case") + assert.Equal(t, n2Record.USN, n2.USN, "n2 USN mismatch for test case") + assert.Equal(t, n2Record.AddedOn, n2.AddedOn, "n2 AddedOn mismatch for test case") + assert.Equal(t, n2Record.EditedOn, n2.EditedOn, "n2 EditedOn mismatch for test case") + assert.Equal(t, n2Record.Body, n2.Body, "n2 Body mismatch for test case") + assert.Equal(t, n2Record.Deleted, n2.Deleted, "n2 Deleted mismatch for test case") + assert.Equal(t, n2Record.Dirty, n2.Dirty, "n2 Dirty mismatch for test case") }) t.Run("local copy has at least one note that is dirty", func(t *testing.T) { b1UUID := utils.GenerateUUID() // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) - db := ctx.DB - - testutils.MustExec(t, "inserting b1 for test case %d", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") - testutils.MustExec(t, "inserting n1 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 10, "n1 body", 1541108743, false, true) + database.MustExec(t, "inserting b1 for test case %d", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") + database.MustExec(t, "inserting n1 for test case %d", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 10, "n1 body", 1541108743, false, true) // execute tx, err := db.Begin() @@ -585,44 +564,42 @@ func TestSyncDeleteBook(t *testing.T) { // test var noteCount, bookCount int - testutils.MustScan(t, "counting notes for test case", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books for test case", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 1, "note count mismatch for test case") - testutils.AssertEqualf(t, bookCount, 1, "book count mismatch for test case") + database.MustScan(t, "counting notes for test case", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books for test case", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + assert.Equalf(t, noteCount, 1, "note count mismatch for test case") + assert.Equalf(t, bookCount, 1, "book count mismatch for test case") - var b1Record core.Book - testutils.MustScan(t, "getting b1 for test case", + var b1Record database.Book + database.MustScan(t, "getting b1 for test case", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", b1UUID), &b1Record.UUID, &b1Record.Label, &b1Record.USN, &b1Record.Dirty) - var n1Record core.Note - testutils.MustScan(t, "getting n1 for test case", + var n1Record database.Note + database.MustScan(t, "getting n1 for test case", db.QueryRow("SELECT uuid, book_uuid, usn, added_on, body,deleted, dirty FROM notes WHERE uuid = ?", "n1-uuid"), &n1Record.UUID, &n1Record.BookUUID, &n1Record.USN, &n1Record.AddedOn, &n1Record.Body, &n1Record.Deleted, &n1Record.Dirty) - testutils.AssertEqual(t, b1Record.UUID, b1UUID, "b1 UUID mismatch for test case") - testutils.AssertEqual(t, b1Record.Label, "b1-label", "b1 Label mismatch for test case") - testutils.AssertEqual(t, b1Record.Dirty, true, "b1 Dirty mismatch for test case") + assert.Equal(t, b1Record.UUID, b1UUID, "b1 UUID mismatch for test case") + assert.Equal(t, b1Record.Label, "b1-label", "b1 Label mismatch for test case") + assert.Equal(t, b1Record.Dirty, true, "b1 Dirty mismatch for test case") - testutils.AssertEqual(t, n1Record.UUID, "n1-uuid", "n1 UUID mismatch for test case") - testutils.AssertEqual(t, n1Record.BookUUID, b1UUID, "n1 BookUUID mismatch for test case") - testutils.AssertEqual(t, n1Record.USN, 10, "n1 USN mismatch for test case") - testutils.AssertEqual(t, n1Record.AddedOn, int64(1541108743), "n1 AddedOn mismatch for test case") - testutils.AssertEqual(t, n1Record.Body, "n1 body", "n1 Body mismatch for test case") - testutils.AssertEqual(t, n1Record.Deleted, false, "n1 Deleted mismatch for test case") - testutils.AssertEqual(t, n1Record.Dirty, true, "n1 Dirty mismatch for test case") + assert.Equal(t, n1Record.UUID, "n1-uuid", "n1 UUID mismatch for test case") + assert.Equal(t, n1Record.BookUUID, b1UUID, "n1 BookUUID mismatch for test case") + assert.Equal(t, n1Record.USN, 10, "n1 USN mismatch for test case") + assert.Equal(t, n1Record.AddedOn, int64(1541108743), "n1 AddedOn mismatch for test case") + assert.Equal(t, n1Record.Body, "n1 body", "n1 Body mismatch for test case") + assert.Equal(t, n1Record.Deleted, false, "n1 Deleted mismatch for test case") + assert.Equal(t, n1Record.Dirty, true, "n1 Dirty mismatch for test case") }) } func TestFullSyncNote(t *testing.T) { t.Run("exists on server only", func(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") // execute tx, err := db.Begin() @@ -649,25 +626,25 @@ func TestFullSyncNote(t *testing.T) { // test var noteCount, bookCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 1, "note count mismatch") - testutils.AssertEqualf(t, bookCount, 1, "book count mismatch") + assert.Equalf(t, noteCount, 1, "note count mismatch") + assert.Equalf(t, bookCount, 1, "book count mismatch") - var n1 core.Note - testutils.MustScan(t, "getting n1", + var n1 database.Note + database.MustScan(t, "getting n1", db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", n.UUID), &n1.UUID, &n1.BookUUID, &n1.USN, &n1.AddedOn, &n1.EditedOn, &n1.Body, &n1.Deleted, &n1.Dirty) - testutils.AssertEqual(t, n1.UUID, n.UUID, "n1 UUID mismatch") - testutils.AssertEqual(t, n1.BookUUID, n.BookUUID, "n1 BookUUID mismatch") - testutils.AssertEqual(t, n1.USN, n.USN, "n1 USN mismatch") - testutils.AssertEqual(t, n1.AddedOn, n.AddedOn, "n1 AddedOn mismatch") - testutils.AssertEqual(t, n1.EditedOn, n.EditedOn, "n1 EditedOn mismatch") - testutils.AssertEqual(t, n1.Body, n.Body, "n1 Body mismatch") - testutils.AssertEqual(t, n1.Deleted, n.Deleted, "n1 Deleted mismatch") - testutils.AssertEqual(t, n1.Dirty, false, "n1 Dirty mismatch") + assert.Equal(t, n1.UUID, n.UUID, "n1 UUID mismatch") + assert.Equal(t, n1.BookUUID, n.BookUUID, "n1 BookUUID mismatch") + assert.Equal(t, n1.USN, n.USN, "n1 USN mismatch") + assert.Equal(t, n1.AddedOn, n.AddedOn, "n1 AddedOn mismatch") + assert.Equal(t, n1.EditedOn, n.EditedOn, "n1 EditedOn mismatch") + assert.Equal(t, n1.Body, n.Body, "n1 Body mismatch") + assert.Equal(t, n1.Deleted, n.Deleted, "n1 Deleted mismatch") + assert.Equal(t, n1.Dirty, false, "n1 Dirty mismatch") }) t.Run("exists on server and client", func(t *testing.T) { @@ -850,16 +827,14 @@ n1 body edited for idx, tc := range testCases { func() { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) - db := ctx.DB - - testutils.MustExec(t, fmt.Sprintf("inserting b1 for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") - testutils.MustExec(t, fmt.Sprintf("inserting b2 for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "b2-label") - testutils.MustExec(t, fmt.Sprintf("inserting conflitcs book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", conflictBookUUID, "conflicts") + database.MustExec(t, fmt.Sprintf("inserting b1 for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") + database.MustExec(t, fmt.Sprintf("inserting b2 for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "b2-label") + database.MustExec(t, fmt.Sprintf("inserting conflitcs book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", conflictBookUUID, "conflicts") n1UUID := utils.GenerateUUID() - testutils.MustExec(t, fmt.Sprintf("inserting n1 for test case %d", idx), db, "INSERT INTO notes (uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", n1UUID, tc.clientBookUUID, tc.clientUSN, tc.addedOn, tc.clientEditedOn, tc.clientBody, tc.clientDeleted, tc.clientDirty) + database.MustExec(t, fmt.Sprintf("inserting n1 for test case %d", idx), db, "INSERT INTO notes (uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", n1UUID, tc.clientBookUUID, tc.clientUSN, tc.addedOn, tc.clientEditedOn, tc.clientBody, tc.clientDeleted, tc.clientDirty) // execute tx, err := db.Begin() @@ -887,25 +862,25 @@ n1 body edited // test var noteCount, bookCount int - testutils.MustScan(t, fmt.Sprintf("counting notes for test case %d", idx), db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, fmt.Sprintf("counting books for test case %d", idx), db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, fmt.Sprintf("counting notes for test case %d", idx), db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, fmt.Sprintf("counting books for test case %d", idx), db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 1, fmt.Sprintf("note count mismatch for test case %d", idx)) - testutils.AssertEqualf(t, bookCount, 3, fmt.Sprintf("book count mismatch for test case %d", idx)) + assert.Equalf(t, noteCount, 1, fmt.Sprintf("note count mismatch for test case %d", idx)) + assert.Equalf(t, bookCount, 3, fmt.Sprintf("book count mismatch for test case %d", idx)) - var n1 core.Note - testutils.MustScan(t, fmt.Sprintf("getting n1 for test case %d", idx), + var n1 database.Note + database.MustScan(t, fmt.Sprintf("getting n1 for test case %d", idx), db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", n.UUID), &n1.UUID, &n1.BookUUID, &n1.USN, &n1.AddedOn, &n1.EditedOn, &n1.Body, &n1.Deleted, &n1.Dirty) - testutils.AssertEqual(t, n1.UUID, n.UUID, fmt.Sprintf("n1 UUID mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1.BookUUID, tc.expectedBookUUID, fmt.Sprintf("n1 BookUUID mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1.USN, tc.expectedUSN, fmt.Sprintf("n1 USN mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1.AddedOn, tc.expectedAddedOn, fmt.Sprintf("n1 AddedOn mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1.EditedOn, tc.expectedEditedOn, fmt.Sprintf("n1 EditedOn mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1.Body, tc.expectedBody, fmt.Sprintf("n1 Body mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1.Deleted, tc.expectedDeleted, fmt.Sprintf("n1 Deleted mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1.Dirty, tc.expectedDirty, fmt.Sprintf("n1 Dirty mismatch for test case %d", idx)) + assert.Equal(t, n1.UUID, n.UUID, fmt.Sprintf("n1 UUID mismatch for test case %d", idx)) + assert.Equal(t, n1.BookUUID, tc.expectedBookUUID, fmt.Sprintf("n1 BookUUID mismatch for test case %d", idx)) + assert.Equal(t, n1.USN, tc.expectedUSN, fmt.Sprintf("n1 USN mismatch for test case %d", idx)) + assert.Equal(t, n1.AddedOn, tc.expectedAddedOn, fmt.Sprintf("n1 AddedOn mismatch for test case %d", idx)) + assert.Equal(t, n1.EditedOn, tc.expectedEditedOn, fmt.Sprintf("n1 EditedOn mismatch for test case %d", idx)) + assert.Equal(t, n1.Body, tc.expectedBody, fmt.Sprintf("n1 Body mismatch for test case %d", idx)) + assert.Equal(t, n1.Deleted, tc.expectedDeleted, fmt.Sprintf("n1 Deleted mismatch for test case %d", idx)) + assert.Equal(t, n1.Dirty, tc.expectedDirty, fmt.Sprintf("n1 Dirty mismatch for test case %d", idx)) }() } }) @@ -914,13 +889,11 @@ n1 body edited func TestFullSyncBook(t *testing.T) { t.Run("exists on server only", func(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", b1UUID, 555, "b1-label", true, false) + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", b1UUID, 555, "b1-label", true, false) // execute tx, err := db.Begin() @@ -946,31 +919,31 @@ func TestFullSyncBook(t *testing.T) { // test var noteCount, bookCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 0, "note count mismatch") - testutils.AssertEqualf(t, bookCount, 2, "book count mismatch") + assert.Equalf(t, noteCount, 0, "note count mismatch") + assert.Equalf(t, bookCount, 2, "book count mismatch") - var b1, b2 core.Book - testutils.MustScan(t, "getting b1", + var b1, b2 database.Book + database.MustScan(t, "getting b1", db.QueryRow("SELECT uuid, usn, label, dirty, deleted FROM books WHERE uuid = ?", b1UUID), &b1.UUID, &b1.USN, &b1.Label, &b1.Dirty, &b1.Deleted) - testutils.MustScan(t, "getting b2", + database.MustScan(t, "getting b2", db.QueryRow("SELECT uuid, usn, label, dirty, deleted FROM books WHERE uuid = ?", b2UUID), &b2.UUID, &b2.USN, &b2.Label, &b2.Dirty, &b2.Deleted) - testutils.AssertEqual(t, b1.UUID, b1UUID, "b1 UUID mismatch") - testutils.AssertEqual(t, b1.USN, 555, "b1 USN mismatch") - testutils.AssertEqual(t, b1.Label, "b1-label", "b1 Label mismatch") - testutils.AssertEqual(t, b1.Dirty, true, "b1 Dirty mismatch") - testutils.AssertEqual(t, b1.Deleted, false, "b1 Deleted mismatch") + assert.Equal(t, b1.UUID, b1UUID, "b1 UUID mismatch") + assert.Equal(t, b1.USN, 555, "b1 USN mismatch") + assert.Equal(t, b1.Label, "b1-label", "b1 Label mismatch") + assert.Equal(t, b1.Dirty, true, "b1 Dirty mismatch") + assert.Equal(t, b1.Deleted, false, "b1 Deleted mismatch") - testutils.AssertEqual(t, b2.UUID, b2UUID, "b2 UUID mismatch") - testutils.AssertEqual(t, b2.USN, b.USN, "b2 USN mismatch") - testutils.AssertEqual(t, b2.Label, b.Label, "b2 Label mismatch") - testutils.AssertEqual(t, b2.Dirty, false, "b2 Dirty mismatch") - testutils.AssertEqual(t, b2.Deleted, b.Deleted, "b2 Deleted mismatch") + assert.Equal(t, b2.UUID, b2UUID, "b2 UUID mismatch") + assert.Equal(t, b2.USN, b.USN, "b2 USN mismatch") + assert.Equal(t, b2.Label, b.Label, "b2 Label mismatch") + assert.Equal(t, b2.Dirty, false, "b2 Dirty mismatch") + assert.Equal(t, b2.Deleted, b.Deleted, "b2 Deleted mismatch") }) t.Run("exists on server and client", func(t *testing.T) { @@ -1055,13 +1028,11 @@ func TestFullSyncBook(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) b1UUID := utils.GenerateUUID() - testutils.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", b1UUID, tc.clientUSN, tc.clientLabel, tc.clientDirty, tc.clientDeleted) + database.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", b1UUID, tc.clientUSN, tc.clientLabel, tc.clientDirty, tc.clientDeleted) // execute tx, err := db.Begin() @@ -1086,22 +1057,22 @@ func TestFullSyncBook(t *testing.T) { // test var noteCount, bookCount int - testutils.MustScan(t, fmt.Sprintf("counting notes for test case %d", idx), db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, fmt.Sprintf("counting books for test case %d", idx), db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, fmt.Sprintf("counting notes for test case %d", idx), db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, fmt.Sprintf("counting books for test case %d", idx), db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 0, fmt.Sprintf("note count mismatch for test case %d", idx)) - testutils.AssertEqualf(t, bookCount, 1, fmt.Sprintf("book count mismatch for test case %d", idx)) + assert.Equalf(t, noteCount, 0, fmt.Sprintf("note count mismatch for test case %d", idx)) + assert.Equalf(t, bookCount, 1, fmt.Sprintf("book count mismatch for test case %d", idx)) - var b1 core.Book - testutils.MustScan(t, "getting b1", + var b1 database.Book + database.MustScan(t, "getting b1", db.QueryRow("SELECT uuid, usn, label, dirty, deleted FROM books WHERE uuid = ?", b1UUID), &b1.UUID, &b1.USN, &b1.Label, &b1.Dirty, &b1.Deleted) - testutils.AssertEqual(t, b1.UUID, b1UUID, fmt.Sprintf("b1 UUID mismatch for idx %d", idx)) - testutils.AssertEqual(t, b1.USN, tc.expectedUSN, fmt.Sprintf("b1 USN mismatch for test case %d", idx)) - testutils.AssertEqual(t, b1.Label, tc.expectedLabel, fmt.Sprintf("b1 Label mismatch for test case %d", idx)) - testutils.AssertEqual(t, b1.Dirty, tc.clientDirty, fmt.Sprintf("b1 Dirty mismatch for test case %d", idx)) - testutils.AssertEqual(t, b1.Deleted, tc.expectedDeleted, fmt.Sprintf("b1 Deleted mismatch for test case %d", idx)) + assert.Equal(t, b1.UUID, b1UUID, fmt.Sprintf("b1 UUID mismatch for idx %d", idx)) + assert.Equal(t, b1.USN, tc.expectedUSN, fmt.Sprintf("b1 USN mismatch for test case %d", idx)) + assert.Equal(t, b1.Label, tc.expectedLabel, fmt.Sprintf("b1 Label mismatch for test case %d", idx)) + assert.Equal(t, b1.Dirty, tc.clientDirty, fmt.Sprintf("b1 Dirty mismatch for test case %d", idx)) + assert.Equal(t, b1.Deleted, tc.expectedDeleted, fmt.Sprintf("b1 Deleted mismatch for test case %d", idx)) }() } }) @@ -1110,13 +1081,11 @@ func TestFullSyncBook(t *testing.T) { func TestStepSyncNote(t *testing.T) { t.Run("exists on server only", func(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") // execute tx, err := db.Begin() @@ -1143,25 +1112,25 @@ func TestStepSyncNote(t *testing.T) { // test var noteCount, bookCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 1, "note count mismatch") - testutils.AssertEqualf(t, bookCount, 1, "book count mismatch") + assert.Equalf(t, noteCount, 1, "note count mismatch") + assert.Equalf(t, bookCount, 1, "book count mismatch") - var n1 core.Note - testutils.MustScan(t, "getting n1", + var n1 database.Note + database.MustScan(t, "getting n1", db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", n.UUID), &n1.UUID, &n1.BookUUID, &n1.USN, &n1.AddedOn, &n1.EditedOn, &n1.Body, &n1.Deleted, &n1.Dirty) - testutils.AssertEqual(t, n1.UUID, n.UUID, "n1 UUID mismatch") - testutils.AssertEqual(t, n1.BookUUID, n.BookUUID, "n1 BookUUID mismatch") - testutils.AssertEqual(t, n1.USN, n.USN, "n1 USN mismatch") - testutils.AssertEqual(t, n1.AddedOn, n.AddedOn, "n1 AddedOn mismatch") - testutils.AssertEqual(t, n1.EditedOn, n.EditedOn, "n1 EditedOn mismatch") - testutils.AssertEqual(t, n1.Body, n.Body, "n1 Body mismatch") - testutils.AssertEqual(t, n1.Deleted, n.Deleted, "n1 Deleted mismatch") - testutils.AssertEqual(t, n1.Dirty, false, "n1 Dirty mismatch") + assert.Equal(t, n1.UUID, n.UUID, "n1 UUID mismatch") + assert.Equal(t, n1.BookUUID, n.BookUUID, "n1 BookUUID mismatch") + assert.Equal(t, n1.USN, n.USN, "n1 USN mismatch") + assert.Equal(t, n1.AddedOn, n.AddedOn, "n1 AddedOn mismatch") + assert.Equal(t, n1.EditedOn, n.EditedOn, "n1 EditedOn mismatch") + assert.Equal(t, n1.Body, n.Body, "n1 Body mismatch") + assert.Equal(t, n1.Deleted, n.Deleted, "n1 Deleted mismatch") + assert.Equal(t, n1.Dirty, false, "n1 Dirty mismatch") }) t.Run("exists on server and client", func(t *testing.T) { @@ -1270,16 +1239,14 @@ n1 body edited for idx, tc := range testCases { func() { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) - db := ctx.DB - - testutils.MustExec(t, fmt.Sprintf("inserting b1 for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") - testutils.MustExec(t, fmt.Sprintf("inserting b2 for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "b2-label") - testutils.MustExec(t, fmt.Sprintf("inserting conflitcs book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", conflictBookUUID, "conflicts") + database.MustExec(t, fmt.Sprintf("inserting b1 for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1-label") + database.MustExec(t, fmt.Sprintf("inserting b2 for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "b2-label") + database.MustExec(t, fmt.Sprintf("inserting conflitcs book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", conflictBookUUID, "conflicts") n1UUID := utils.GenerateUUID() - testutils.MustExec(t, fmt.Sprintf("inserting n1 for test case %d", idx), db, "INSERT INTO notes (uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", n1UUID, tc.clientBookUUID, tc.clientUSN, tc.addedOn, tc.clientEditedOn, tc.clientBody, tc.clientDeleted, tc.clientDirty) + database.MustExec(t, fmt.Sprintf("inserting n1 for test case %d", idx), db, "INSERT INTO notes (uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", n1UUID, tc.clientBookUUID, tc.clientUSN, tc.addedOn, tc.clientEditedOn, tc.clientBody, tc.clientDeleted, tc.clientDirty) // execute tx, err := db.Begin() @@ -1307,25 +1274,25 @@ n1 body edited // test var noteCount, bookCount int - testutils.MustScan(t, fmt.Sprintf("counting notes for test case %d", idx), db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, fmt.Sprintf("counting books for test case %d", idx), db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, fmt.Sprintf("counting notes for test case %d", idx), db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, fmt.Sprintf("counting books for test case %d", idx), db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 1, fmt.Sprintf("note count mismatch for test case %d", idx)) - testutils.AssertEqualf(t, bookCount, 3, fmt.Sprintf("book count mismatch for test case %d", idx)) + assert.Equalf(t, noteCount, 1, fmt.Sprintf("note count mismatch for test case %d", idx)) + assert.Equalf(t, bookCount, 3, fmt.Sprintf("book count mismatch for test case %d", idx)) - var n1 core.Note - testutils.MustScan(t, fmt.Sprintf("getting n1 for test case %d", idx), + var n1 database.Note + database.MustScan(t, fmt.Sprintf("getting n1 for test case %d", idx), db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", n.UUID), &n1.UUID, &n1.BookUUID, &n1.USN, &n1.AddedOn, &n1.EditedOn, &n1.Body, &n1.Deleted, &n1.Dirty) - testutils.AssertEqual(t, n1.UUID, n.UUID, fmt.Sprintf("n1 UUID mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1.BookUUID, tc.expectedBookUUID, fmt.Sprintf("n1 BookUUID mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1.USN, tc.expectedUSN, fmt.Sprintf("n1 USN mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1.AddedOn, tc.expectedAddedOn, fmt.Sprintf("n1 AddedOn mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1.EditedOn, tc.expectedEditedOn, fmt.Sprintf("n1 EditedOn mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1.Body, tc.expectedBody, fmt.Sprintf("n1 Body mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1.Deleted, tc.expectedDeleted, fmt.Sprintf("n1 Deleted mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1.Dirty, tc.expectedDirty, fmt.Sprintf("n1 Dirty mismatch for test case %d", idx)) + assert.Equal(t, n1.UUID, n.UUID, fmt.Sprintf("n1 UUID mismatch for test case %d", idx)) + assert.Equal(t, n1.BookUUID, tc.expectedBookUUID, fmt.Sprintf("n1 BookUUID mismatch for test case %d", idx)) + assert.Equal(t, n1.USN, tc.expectedUSN, fmt.Sprintf("n1 USN mismatch for test case %d", idx)) + assert.Equal(t, n1.AddedOn, tc.expectedAddedOn, fmt.Sprintf("n1 AddedOn mismatch for test case %d", idx)) + assert.Equal(t, n1.EditedOn, tc.expectedEditedOn, fmt.Sprintf("n1 EditedOn mismatch for test case %d", idx)) + assert.Equal(t, n1.Body, tc.expectedBody, fmt.Sprintf("n1 Body mismatch for test case %d", idx)) + assert.Equal(t, n1.Deleted, tc.expectedDeleted, fmt.Sprintf("n1 Deleted mismatch for test case %d", idx)) + assert.Equal(t, n1.Dirty, tc.expectedDirty, fmt.Sprintf("n1 Dirty mismatch for test case %d", idx)) }() } }) @@ -1334,13 +1301,11 @@ n1 body edited func TestStepSyncBook(t *testing.T) { t.Run("exists on server only", func(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", b1UUID, 555, "b1-label", true, false) + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", b1UUID, 555, "b1-label", true, false) // execute tx, err := db.Begin() @@ -1366,31 +1331,31 @@ func TestStepSyncBook(t *testing.T) { // test var noteCount, bookCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 0, "note count mismatch") - testutils.AssertEqualf(t, bookCount, 2, "book count mismatch") + assert.Equalf(t, noteCount, 0, "note count mismatch") + assert.Equalf(t, bookCount, 2, "book count mismatch") - var b1, b2 core.Book - testutils.MustScan(t, "getting b1", + var b1, b2 database.Book + database.MustScan(t, "getting b1", db.QueryRow("SELECT uuid, usn, label, dirty, deleted FROM books WHERE uuid = ?", b1UUID), &b1.UUID, &b1.USN, &b1.Label, &b1.Dirty, &b1.Deleted) - testutils.MustScan(t, "getting b2", + database.MustScan(t, "getting b2", db.QueryRow("SELECT uuid, usn, label, dirty, deleted FROM books WHERE uuid = ?", b2UUID), &b2.UUID, &b2.USN, &b2.Label, &b2.Dirty, &b2.Deleted) - testutils.AssertEqual(t, b1.UUID, b1UUID, "b1 UUID mismatch") - testutils.AssertEqual(t, b1.USN, 555, "b1 USN mismatch") - testutils.AssertEqual(t, b1.Label, "b1-label", "b1 Label mismatch") - testutils.AssertEqual(t, b1.Dirty, true, "b1 Dirty mismatch") - testutils.AssertEqual(t, b1.Deleted, false, "b1 Deleted mismatch") + assert.Equal(t, b1.UUID, b1UUID, "b1 UUID mismatch") + assert.Equal(t, b1.USN, 555, "b1 USN mismatch") + assert.Equal(t, b1.Label, "b1-label", "b1 Label mismatch") + assert.Equal(t, b1.Dirty, true, "b1 Dirty mismatch") + assert.Equal(t, b1.Deleted, false, "b1 Deleted mismatch") - testutils.AssertEqual(t, b2.UUID, b2UUID, "b2 UUID mismatch") - testutils.AssertEqual(t, b2.USN, b.USN, "b2 USN mismatch") - testutils.AssertEqual(t, b2.Label, b.Label, "b2 Label mismatch") - testutils.AssertEqual(t, b2.Dirty, false, "b2 Dirty mismatch") - testutils.AssertEqual(t, b2.Deleted, b.Deleted, "b2 Deleted mismatch") + assert.Equal(t, b2.UUID, b2UUID, "b2 UUID mismatch") + assert.Equal(t, b2.USN, b.USN, "b2 USN mismatch") + assert.Equal(t, b2.Label, b.Label, "b2 Label mismatch") + assert.Equal(t, b2.Dirty, false, "b2 Dirty mismatch") + assert.Equal(t, b2.Deleted, b.Deleted, "b2 Deleted mismatch") }) t.Run("exists on server and client", func(t *testing.T) { @@ -1459,15 +1424,13 @@ func TestStepSyncBook(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) b1UUID := utils.GenerateUUID() - testutils.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", b1UUID, tc.clientUSN, tc.clientLabel, tc.clientDirty, tc.clientDeleted) + database.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", b1UUID, tc.clientUSN, tc.clientLabel, tc.clientDirty, tc.clientDeleted) b2UUID := utils.GenerateUUID() - testutils.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", b2UUID, 2, tc.anotherBookLabel, false, false) + database.MustExec(t, fmt.Sprintf("inserting book for test case %d", idx), db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", b2UUID, 2, tc.anotherBookLabel, false, false) // execute tx, err := db.Begin() @@ -1492,31 +1455,31 @@ func TestStepSyncBook(t *testing.T) { // test var noteCount, bookCount int - testutils.MustScan(t, fmt.Sprintf("counting notes for test case %d", idx), db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, fmt.Sprintf("counting books for test case %d", idx), db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, fmt.Sprintf("counting notes for test case %d", idx), db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, fmt.Sprintf("counting books for test case %d", idx), db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 0, fmt.Sprintf("note count mismatch for test case %d", idx)) - testutils.AssertEqualf(t, bookCount, 2, fmt.Sprintf("book count mismatch for test case %d", idx)) + assert.Equalf(t, noteCount, 0, fmt.Sprintf("note count mismatch for test case %d", idx)) + assert.Equalf(t, bookCount, 2, fmt.Sprintf("book count mismatch for test case %d", idx)) - var b1Record, b2Record core.Book - testutils.MustScan(t, "getting b1Record", + var b1Record, b2Record database.Book + database.MustScan(t, "getting b1Record", db.QueryRow("SELECT uuid, usn, label, dirty, deleted FROM books WHERE uuid = ?", b1UUID), &b1Record.UUID, &b1Record.USN, &b1Record.Label, &b1Record.Dirty, &b1Record.Deleted) - testutils.MustScan(t, "getting b2Record", + database.MustScan(t, "getting b2Record", db.QueryRow("SELECT uuid, usn, label, dirty, deleted FROM books WHERE uuid = ?", b2UUID), &b2Record.UUID, &b2Record.USN, &b2Record.Label, &b2Record.Dirty, &b2Record.Deleted) - testutils.AssertEqual(t, b1Record.UUID, b1UUID, fmt.Sprintf("b1Record UUID mismatch for idx %d", idx)) - testutils.AssertEqual(t, b1Record.USN, tc.expectedUSN, fmt.Sprintf("b1Record USN mismatch for test case %d", idx)) - testutils.AssertEqual(t, b1Record.Label, tc.expectedLabel, fmt.Sprintf("b1Record Label mismatch for test case %d", idx)) - testutils.AssertEqual(t, b1Record.Dirty, tc.clientDirty, fmt.Sprintf("b1Record Dirty mismatch for test case %d", idx)) - testutils.AssertEqual(t, b1Record.Deleted, tc.expectedDeleted, fmt.Sprintf("b1Record Deleted mismatch for test case %d", idx)) + assert.Equal(t, b1Record.UUID, b1UUID, fmt.Sprintf("b1Record UUID mismatch for idx %d", idx)) + assert.Equal(t, b1Record.USN, tc.expectedUSN, fmt.Sprintf("b1Record USN mismatch for test case %d", idx)) + assert.Equal(t, b1Record.Label, tc.expectedLabel, fmt.Sprintf("b1Record Label mismatch for test case %d", idx)) + assert.Equal(t, b1Record.Dirty, tc.clientDirty, fmt.Sprintf("b1Record Dirty mismatch for test case %d", idx)) + assert.Equal(t, b1Record.Deleted, tc.expectedDeleted, fmt.Sprintf("b1Record Deleted mismatch for test case %d", idx)) - testutils.AssertEqual(t, b2Record.UUID, b2UUID, fmt.Sprintf("b2Record UUID mismatch for idx %d", idx)) - testutils.AssertEqual(t, b2Record.USN, 2, fmt.Sprintf("b2Record USN mismatch for test case %d", idx)) - testutils.AssertEqual(t, b2Record.Label, tc.expectedAnotherBookLabel, fmt.Sprintf("b2Record Label mismatch for test case %d", idx)) - testutils.AssertEqual(t, b2Record.Dirty, tc.expectedAnotherBookDirty, fmt.Sprintf("b2Record Dirty mismatch for test case %d", idx)) - testutils.AssertEqual(t, b2Record.Deleted, false, fmt.Sprintf("b2Record Deleted mismatch for test case %d", idx)) + assert.Equal(t, b2Record.UUID, b2UUID, fmt.Sprintf("b2Record UUID mismatch for idx %d", idx)) + assert.Equal(t, b2Record.USN, 2, fmt.Sprintf("b2Record USN mismatch for test case %d", idx)) + assert.Equal(t, b2Record.Label, tc.expectedAnotherBookLabel, fmt.Sprintf("b2Record Label mismatch for test case %d", idx)) + assert.Equal(t, b2Record.Dirty, tc.expectedAnotherBookDirty, fmt.Sprintf("b2Record Dirty mismatch for test case %d", idx)) + assert.Equal(t, b2Record.Deleted, false, fmt.Sprintf("b2Record Deleted mismatch for test case %d", idx)) }() } }) @@ -1525,10 +1488,8 @@ func TestStepSyncBook(t *testing.T) { func TestMergeBook(t *testing.T) { t.Run("insert, no duplicates", func(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) // test tx, err := db.Begin() @@ -1553,30 +1514,27 @@ func TestMergeBook(t *testing.T) { // execute var noteCount, bookCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 0, "note count mismatch") - testutils.AssertEqualf(t, bookCount, 1, "book count mismatch") + assert.Equalf(t, noteCount, 0, "note count mismatch") + assert.Equalf(t, bookCount, 1, "book count mismatch") - var b1Record core.Book - testutils.MustScan(t, "getting b1", + var b1Record database.Book + database.MustScan(t, "getting b1", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b1-uuid"), &b1Record.UUID, &b1Record.Label, &b1Record.USN, &b1Record.Dirty) - testutils.AssertEqual(t, b1Record.UUID, b1.UUID, "b1 UUID mismatch") - testutils.AssertEqual(t, b1Record.Label, b1.Label, "b1 Label mismatch") - testutils.AssertEqual(t, b1Record.USN, b1.USN, "b1 USN mismatch") + assert.Equal(t, b1Record.UUID, b1.UUID, "b1 UUID mismatch") + assert.Equal(t, b1Record.Label, b1.Label, "b1 Label mismatch") + assert.Equal(t, b1Record.USN, b1.USN, "b1 USN mismatch") }) t.Run("insert, 1 duplicate", func(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB - - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b1-uuid", 1, "foo", false, false) + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b1-uuid", 1, "foo", false, false) // test tx, err := db.Begin() @@ -1601,39 +1559,37 @@ func TestMergeBook(t *testing.T) { // execute var noteCount, bookCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 0, "note count mismatch") - testutils.AssertEqualf(t, bookCount, 2, "book count mismatch") + assert.Equalf(t, noteCount, 0, "note count mismatch") + assert.Equalf(t, bookCount, 2, "book count mismatch") - var b1Record, b2Record core.Book - testutils.MustScan(t, "getting b1", + var b1Record, b2Record database.Book + database.MustScan(t, "getting b1", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b1-uuid"), &b1Record.UUID, &b1Record.Label, &b1Record.USN, &b1Record.Dirty) - testutils.MustScan(t, "getting b2", + database.MustScan(t, "getting b2", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b2-uuid"), &b2Record.UUID, &b2Record.Label, &b2Record.USN, &b2Record.Dirty) - testutils.AssertEqual(t, b1Record.Label, "foo_2", "b1 Label mismatch") - testutils.AssertEqual(t, b1Record.USN, 1, "b1 USN mismatch") - testutils.AssertEqual(t, b1Record.Dirty, true, "b1 should have been marked dirty") + assert.Equal(t, b1Record.Label, "foo_2", "b1 Label mismatch") + assert.Equal(t, b1Record.USN, 1, "b1 USN mismatch") + assert.Equal(t, b1Record.Dirty, true, "b1 should have been marked dirty") - testutils.AssertEqual(t, b2Record.Label, "foo", "b2 Label mismatch") - testutils.AssertEqual(t, b2Record.USN, 12, "b2 USN mismatch") - testutils.AssertEqual(t, b2Record.Dirty, false, "b2 Dirty mismatch") + assert.Equal(t, b2Record.Label, "foo", "b2 Label mismatch") + assert.Equal(t, b2Record.USN, 12, "b2 USN mismatch") + assert.Equal(t, b2Record.Dirty, false, "b2 Dirty mismatch") }) t.Run("insert, 3 duplicates", func(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) - db := ctx.DB - - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b1-uuid", 1, "foo", false, false) - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b2-uuid", 2, "foo_2", true, false) - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b3-uuid", 3, "foo_3", false, false) + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b1-uuid", 1, "foo", false, false) + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b2-uuid", 2, "foo_2", true, false) + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b3-uuid", 3, "foo_3", false, false) // test tx, err := db.Begin() @@ -1658,49 +1614,47 @@ func TestMergeBook(t *testing.T) { // execute var noteCount, bookCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 0, "note count mismatch") - testutils.AssertEqualf(t, bookCount, 4, "book count mismatch") + assert.Equalf(t, noteCount, 0, "note count mismatch") + assert.Equalf(t, bookCount, 4, "book count mismatch") - var b1Record, b2Record, b3Record, b4Record core.Book - testutils.MustScan(t, "getting b1", + var b1Record, b2Record, b3Record, b4Record database.Book + database.MustScan(t, "getting b1", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b1-uuid"), &b1Record.UUID, &b1Record.Label, &b1Record.USN, &b1Record.Dirty) - testutils.MustScan(t, "getting b2", + database.MustScan(t, "getting b2", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b2-uuid"), &b2Record.UUID, &b2Record.Label, &b2Record.USN, &b2Record.Dirty) - testutils.MustScan(t, "getting b3", + database.MustScan(t, "getting b3", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b3-uuid"), &b3Record.UUID, &b3Record.Label, &b3Record.USN, &b3Record.Dirty) - testutils.MustScan(t, "getting b4", + database.MustScan(t, "getting b4", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b4-uuid"), &b4Record.UUID, &b4Record.Label, &b4Record.USN, &b4Record.Dirty) - testutils.AssertEqual(t, b1Record.Label, "foo_4", "b1 Label mismatch") - testutils.AssertEqual(t, b1Record.USN, 1, "b1 USN mismatch") - testutils.AssertEqual(t, b1Record.Dirty, true, "b1 Dirty mismatch") + assert.Equal(t, b1Record.Label, "foo_4", "b1 Label mismatch") + assert.Equal(t, b1Record.USN, 1, "b1 USN mismatch") + assert.Equal(t, b1Record.Dirty, true, "b1 Dirty mismatch") - testutils.AssertEqual(t, b2Record.Label, "foo_2", "b2 Label mismatch") - testutils.AssertEqual(t, b2Record.USN, 2, "b2 USN mismatch") - testutils.AssertEqual(t, b2Record.Dirty, true, "b2 Dirty mismatch") + assert.Equal(t, b2Record.Label, "foo_2", "b2 Label mismatch") + assert.Equal(t, b2Record.USN, 2, "b2 USN mismatch") + assert.Equal(t, b2Record.Dirty, true, "b2 Dirty mismatch") - testutils.AssertEqual(t, b3Record.Label, "foo_3", "b3 Label mismatch") - testutils.AssertEqual(t, b3Record.USN, 3, "b3 USN mismatch") - testutils.AssertEqual(t, b3Record.Dirty, false, "b3 Dirty mismatch") + assert.Equal(t, b3Record.Label, "foo_3", "b3 Label mismatch") + assert.Equal(t, b3Record.USN, 3, "b3 USN mismatch") + assert.Equal(t, b3Record.Dirty, false, "b3 Dirty mismatch") - testutils.AssertEqual(t, b4Record.Label, "foo", "b4 Label mismatch") - testutils.AssertEqual(t, b4Record.USN, 12, "b4 USN mismatch") - testutils.AssertEqual(t, b4Record.Dirty, false, "b4 Dirty mismatch") + assert.Equal(t, b4Record.Label, "foo", "b4 Label mismatch") + assert.Equal(t, b4Record.USN, 12, "b4 USN mismatch") + assert.Equal(t, b4Record.Dirty, false, "b4 Dirty mismatch") }) t.Run("update, no duplicates", func(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) // test tx, err := db.Begin() @@ -1709,7 +1663,7 @@ func TestMergeBook(t *testing.T) { } b1UUID := utils.GenerateUUID() - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", b1UUID, 1, "b1-label", false, false) + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", b1UUID, 1, "b1-label", false, false) b1 := client.SyncFragBook{ UUID: b1UUID, @@ -1728,31 +1682,29 @@ func TestMergeBook(t *testing.T) { // execute var noteCount, bookCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 0, "note count mismatch") - testutils.AssertEqualf(t, bookCount, 1, "book count mismatch") + assert.Equalf(t, noteCount, 0, "note count mismatch") + assert.Equalf(t, bookCount, 1, "book count mismatch") - var b1Record core.Book - testutils.MustScan(t, "getting b1", + var b1Record database.Book + database.MustScan(t, "getting b1", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", b1UUID), &b1Record.UUID, &b1Record.Label, &b1Record.USN, &b1Record.Dirty) - testutils.AssertEqual(t, b1Record.UUID, b1UUID, "b1 UUID mismatch") - testutils.AssertEqual(t, b1Record.Label, "b1-label-edited", "b1 Label mismatch") - testutils.AssertEqual(t, b1Record.USN, 12, "b1 USN mismatch") + assert.Equal(t, b1Record.UUID, b1UUID, "b1 UUID mismatch") + assert.Equal(t, b1Record.Label, "b1-label-edited", "b1 Label mismatch") + assert.Equal(t, b1Record.USN, 12, "b1 USN mismatch") }) t.Run("update, 1 duplicate", func(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) - db := ctx.DB - - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b1-uuid", 1, "foo", false, false) - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b2-uuid", 2, "bar", false, false) + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b1-uuid", 1, "foo", false, false) + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b2-uuid", 2, "bar", false, false) // test tx, err := db.Begin() @@ -1777,40 +1729,38 @@ func TestMergeBook(t *testing.T) { // execute var noteCount, bookCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 0, "note count mismatch") - testutils.AssertEqualf(t, bookCount, 2, "book count mismatch") + assert.Equalf(t, noteCount, 0, "note count mismatch") + assert.Equalf(t, bookCount, 2, "book count mismatch") - var b1Record, b2Record core.Book - testutils.MustScan(t, "getting b1", + var b1Record, b2Record database.Book + database.MustScan(t, "getting b1", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b1-uuid"), &b1Record.UUID, &b1Record.Label, &b1Record.USN, &b1Record.Dirty) - testutils.MustScan(t, "getting b2", + database.MustScan(t, "getting b2", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b2-uuid"), &b2Record.UUID, &b2Record.Label, &b2Record.USN, &b2Record.Dirty) - testutils.AssertEqual(t, b1Record.Label, "bar", "b1 Label mismatch") - testutils.AssertEqual(t, b1Record.USN, 12, "b1 USN mismatch") - testutils.AssertEqual(t, b1Record.Dirty, false, "b1 Dirty mismatch") + assert.Equal(t, b1Record.Label, "bar", "b1 Label mismatch") + assert.Equal(t, b1Record.USN, 12, "b1 USN mismatch") + assert.Equal(t, b1Record.Dirty, false, "b1 Dirty mismatch") - testutils.AssertEqual(t, b2Record.Label, "bar_2", "b2 Label mismatch") - testutils.AssertEqual(t, b2Record.USN, 2, "b2 USN mismatch") - testutils.AssertEqual(t, b2Record.Dirty, true, "b2 Dirty mismatch") + assert.Equal(t, b2Record.Label, "bar_2", "b2 Label mismatch") + assert.Equal(t, b2Record.USN, 2, "b2 USN mismatch") + assert.Equal(t, b2Record.Dirty, true, "b2 Dirty mismatch") }) t.Run("update, 3 duplicate", func(t *testing.T) { // set uj - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, dbPath, nil) + defer database.CloseTestDB(t, db) - db := ctx.DB - - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b1-uuid", 1, "foo", false, false) - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b2-uuid", 2, "bar", false, false) - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b3-uuid", 3, "bar_2", true, false) - testutils.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b4-uuid", 4, "bar_3", false, false) + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b1-uuid", 1, "foo", false, false) + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b2-uuid", 2, "bar", false, false) + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b3-uuid", 3, "bar_2", true, false) + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, usn, label, dirty, deleted) VALUES (?, ?, ?, ?, ?)", "b4-uuid", 4, "bar_3", false, false) // test tx, err := db.Begin() @@ -1835,54 +1785,54 @@ func TestMergeBook(t *testing.T) { // execute var noteCount, bookCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 0, "note count mismatch") - testutils.AssertEqualf(t, bookCount, 4, "book count mismatch") + assert.Equalf(t, noteCount, 0, "note count mismatch") + assert.Equalf(t, bookCount, 4, "book count mismatch") - var b1Record, b2Record, b3Record, b4Record core.Book - testutils.MustScan(t, "getting b1", + var b1Record, b2Record, b3Record, b4Record database.Book + database.MustScan(t, "getting b1", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b1-uuid"), &b1Record.UUID, &b1Record.Label, &b1Record.USN, &b1Record.Dirty) - testutils.MustScan(t, "getting b2", + database.MustScan(t, "getting b2", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b2-uuid"), &b2Record.UUID, &b2Record.Label, &b2Record.USN, &b2Record.Dirty) - testutils.MustScan(t, "getting b3", + database.MustScan(t, "getting b3", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b3-uuid"), &b3Record.UUID, &b3Record.Label, &b3Record.USN, &b3Record.Dirty) - testutils.MustScan(t, "getting b4", + database.MustScan(t, "getting b4", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", "b4-uuid"), &b4Record.UUID, &b4Record.Label, &b4Record.USN, &b4Record.Dirty) - testutils.AssertEqual(t, b1Record.Label, "bar", "b1 Label mismatch") - testutils.AssertEqual(t, b1Record.USN, 12, "b1 USN mismatch") - testutils.AssertEqual(t, b1Record.Dirty, false, "b1 Dirty mismatch") + assert.Equal(t, b1Record.Label, "bar", "b1 Label mismatch") + assert.Equal(t, b1Record.USN, 12, "b1 USN mismatch") + assert.Equal(t, b1Record.Dirty, false, "b1 Dirty mismatch") - testutils.AssertEqual(t, b2Record.Label, "bar_4", "b2 Label mismatch") - testutils.AssertEqual(t, b2Record.USN, 2, "b2 USN mismatch") - testutils.AssertEqual(t, b2Record.Dirty, true, "b2 Dirty mismatch") + assert.Equal(t, b2Record.Label, "bar_4", "b2 Label mismatch") + assert.Equal(t, b2Record.USN, 2, "b2 USN mismatch") + assert.Equal(t, b2Record.Dirty, true, "b2 Dirty mismatch") - testutils.AssertEqual(t, b3Record.Label, "bar_2", "b3 Label mismatch") - testutils.AssertEqual(t, b3Record.USN, 3, "b3 USN mismatch") - testutils.AssertEqual(t, b3Record.Dirty, true, "b3 Dirty mismatch") + assert.Equal(t, b3Record.Label, "bar_2", "b3 Label mismatch") + assert.Equal(t, b3Record.USN, 3, "b3 USN mismatch") + assert.Equal(t, b3Record.Dirty, true, "b3 Dirty mismatch") - testutils.AssertEqual(t, b4Record.Label, "bar_3", "b4 Label mismatch") - testutils.AssertEqual(t, b4Record.USN, 4, "b4 USN mismatch") - testutils.AssertEqual(t, b4Record.Dirty, false, "b4 Dirty mismatch") + assert.Equal(t, b4Record.Label, "bar_3", "b4 Label mismatch") + assert.Equal(t, b4Record.USN, 4, "b4 USN mismatch") + assert.Equal(t, b4Record.Dirty, false, "b4 Dirty mismatch") }) } func TestSaveServerState(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) + ctx := context.InitTestCtx(t, "../../tmp", nil) + defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) - defer testutils.TeardownEnv(ctx) db := ctx.DB - testutils.MustExec(t, "inserting last synced at", db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemLastSyncAt, int64(1231108742)) - testutils.MustExec(t, "inserting last max usn", db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemLastMaxUSN, 8) + database.MustExec(t, "inserting last synced at", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemLastSyncAt, int64(1231108742)) + database.MustExec(t, "inserting last max usn", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemLastMaxUSN, 8) // execute tx, err := db.Begin() @@ -1905,13 +1855,13 @@ func TestSaveServerState(t *testing.T) { var lastSyncedAt int64 var lastMaxUSN int - testutils.MustScan(t, "getting system value", - db.QueryRow("SELECT value FROM system WHERE key = ?", infra.SystemLastSyncAt), &lastSyncedAt) - testutils.MustScan(t, "getting system value", - db.QueryRow("SELECT value FROM system WHERE key = ?", infra.SystemLastMaxUSN), &lastMaxUSN) + database.MustScan(t, "getting system value", + db.QueryRow("SELECT value FROM system WHERE key = ?", consts.SystemLastSyncAt), &lastSyncedAt) + database.MustScan(t, "getting system value", + db.QueryRow("SELECT value FROM system WHERE key = ?", consts.SystemLastMaxUSN), &lastMaxUSN) - testutils.AssertEqual(t, lastSyncedAt, serverTime, "last synced at mismatch") - testutils.AssertEqual(t, lastMaxUSN, serverMaxUSN, "last max usn mismatch") + assert.Equal(t, lastSyncedAt, serverTime, "last synced at mismatch") + assert.Equal(t, lastMaxUSN, serverMaxUSN, "last max usn mismatch") } // TestSendBooks tests that books are put to correct 'buckets' by running a test server and recording the @@ -1919,37 +1869,37 @@ func TestSaveServerState(t *testing.T) { // are updated accordingly based on the server response. func TestSendBooks(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) + ctx := context.InitTestCtx(t, "../../tmp", nil) + defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) - defer testutils.TeardownEnv(ctx) db := ctx.DB - testutils.MustExec(t, "inserting last max usn", db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemLastMaxUSN, 0) + database.MustExec(t, "inserting last max usn", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemLastMaxUSN, 0) // should be ignored - testutils.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 1, false, false) - testutils.MustExec(t, "inserting b2", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b2-uuid", "b2-label", 2, false, false) + database.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 1, false, false) + database.MustExec(t, "inserting b2", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b2-uuid", "b2-label", 2, false, false) // should be created - testutils.MustExec(t, "inserting b3", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b3-uuid", "b3-label", 0, false, true) - testutils.MustExec(t, "inserting b4", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b4-uuid", "b4-label", 0, false, true) + database.MustExec(t, "inserting b3", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b3-uuid", "b3-label", 0, false, true) + database.MustExec(t, "inserting b4", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b4-uuid", "b4-label", 0, false, true) // should be only expunged locally without syncing to server - testutils.MustExec(t, "inserting b5", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b5-uuid", "b5-label", 0, true, true) + database.MustExec(t, "inserting b5", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b5-uuid", "b5-label", 0, true, true) // should be deleted - testutils.MustExec(t, "inserting b6", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b6-uuid", "b6-label", 10, true, true) + database.MustExec(t, "inserting b6", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b6-uuid", "b6-label", 10, true, true) // should be updated - testutils.MustExec(t, "inserting b7", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b7-uuid", "b7-label", 11, false, true) - testutils.MustExec(t, "inserting b8", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b8-uuid", "b8-label", 18, false, true) + database.MustExec(t, "inserting b7", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b7-uuid", "b7-label", 11, false, true) + database.MustExec(t, "inserting b8", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b8-uuid", "b8-label", 18, false, true) // some random notes - testutils.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", "b1-uuid", 10, "n1 body", 1541108743, false, false) - testutils.MustExec(t, "inserting n2", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n2-uuid", "b5-uuid", 10, "n2 body", 1541108743, false, false) - testutils.MustExec(t, "inserting n3", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n3-uuid", "b6-uuid", 10, "n3 body", 1541108743, false, false) - testutils.MustExec(t, "inserting n4", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n4-uuid", "b7-uuid", 10, "n4 body", 1541108743, false, false) + database.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", "b1-uuid", 10, "n1 body", 1541108743, false, false) + database.MustExec(t, "inserting n2", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n2-uuid", "b5-uuid", 10, "n2 body", 1541108743, false, false) + database.MustExec(t, "inserting n3", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n3-uuid", "b6-uuid", 10, "n3 body", 1541108743, false, false) + database.MustExec(t, "inserting n4", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n4-uuid", "b7-uuid", 10, "n4 body", 1541108743, false, false) // notes that belong to the created book. Their book_uuid should be updated. - testutils.MustExec(t, "inserting n5", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n5-uuid", "b3-uuid", 10, "n5 body", 1541108743, false, false) - testutils.MustExec(t, "inserting n6", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n6-uuid", "b3-uuid", 10, "n6 body", 1541108743, false, false) - testutils.MustExec(t, "inserting n7", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n7-uuid", "b4-uuid", 10, "n7 body", 1541108743, false, false) + database.MustExec(t, "inserting n5", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n5-uuid", "b3-uuid", 10, "n5 body", 1541108743, false, false) + database.MustExec(t, "inserting n6", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n6-uuid", "b3-uuid", 10, "n6 body", 1541108743, false, false) + database.MustExec(t, "inserting n7", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n7-uuid", "b4-uuid", 10, "n7 body", 1541108743, false, false) var createdLabels []string var updatesUUIDs []string @@ -2034,51 +1984,51 @@ func TestSendBooks(t *testing.T) { return strings.Compare(createdLabels[i], createdLabels[j]) < 0 }) - testutils.AssertDeepEqual(t, createdLabels, []string{"b3-label", "b4-label"}, "createdLabels mismatch") - testutils.AssertDeepEqual(t, updatesUUIDs, []string{"b7-uuid", "b8-uuid"}, "updatesUUIDs mismatch") - testutils.AssertDeepEqual(t, deletedUUIDs, []string{"b6-uuid"}, "deletedUUIDs mismatch") + assert.DeepEqual(t, createdLabels, []string{"b3-label", "b4-label"}, "createdLabels mismatch") + assert.DeepEqual(t, updatesUUIDs, []string{"b7-uuid", "b8-uuid"}, "updatesUUIDs mismatch") + assert.DeepEqual(t, deletedUUIDs, []string{"b6-uuid"}, "deletedUUIDs mismatch") - var b1, b2, b3, b4, b7, b8 core.Book - testutils.MustScan(t, "getting b1", db.QueryRow("SELECT uuid, dirty FROM books WHERE label = ?", "b1-label"), &b1.UUID, &b1.Dirty) - testutils.MustScan(t, "getting b2", db.QueryRow("SELECT uuid, dirty FROM books WHERE label = ?", "b2-label"), &b2.UUID, &b2.Dirty) - testutils.MustScan(t, "getting b3", db.QueryRow("SELECT uuid, dirty FROM books WHERE label = ?", "b3-label"), &b3.UUID, &b3.Dirty) - testutils.MustScan(t, "getting b4", db.QueryRow("SELECT uuid, dirty FROM books WHERE label = ?", "b4-label"), &b4.UUID, &b4.Dirty) - testutils.MustScan(t, "getting b7", db.QueryRow("SELECT uuid, dirty FROM books WHERE label = ?", "b7-label"), &b7.UUID, &b7.Dirty) - testutils.MustScan(t, "getting b8", db.QueryRow("SELECT uuid, dirty FROM books WHERE label = ?", "b8-label"), &b8.UUID, &b8.Dirty) + var b1, b2, b3, b4, b7, b8 database.Book + database.MustScan(t, "getting b1", db.QueryRow("SELECT uuid, dirty FROM books WHERE label = ?", "b1-label"), &b1.UUID, &b1.Dirty) + database.MustScan(t, "getting b2", db.QueryRow("SELECT uuid, dirty FROM books WHERE label = ?", "b2-label"), &b2.UUID, &b2.Dirty) + database.MustScan(t, "getting b3", db.QueryRow("SELECT uuid, dirty FROM books WHERE label = ?", "b3-label"), &b3.UUID, &b3.Dirty) + database.MustScan(t, "getting b4", db.QueryRow("SELECT uuid, dirty FROM books WHERE label = ?", "b4-label"), &b4.UUID, &b4.Dirty) + database.MustScan(t, "getting b7", db.QueryRow("SELECT uuid, dirty FROM books WHERE label = ?", "b7-label"), &b7.UUID, &b7.Dirty) + database.MustScan(t, "getting b8", db.QueryRow("SELECT uuid, dirty FROM books WHERE label = ?", "b8-label"), &b8.UUID, &b8.Dirty) var bookCount int - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, bookCount, 6, "book count mismatch") + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + assert.Equalf(t, bookCount, 6, "book count mismatch") - testutils.AssertEqual(t, b1.Dirty, false, "b1 Dirty mismatch") - testutils.AssertEqual(t, b2.Dirty, false, "b2 Dirty mismatch") - testutils.AssertEqual(t, b3.Dirty, false, "b3 Dirty mismatch") - testutils.AssertEqual(t, b4.Dirty, false, "b4 Dirty mismatch") - testutils.AssertEqual(t, b7.Dirty, false, "b7 Dirty mismatch") - testutils.AssertEqual(t, b8.Dirty, false, "b8 Dirty mismatch") - testutils.AssertEqual(t, b1.UUID, "b1-uuid", "b1 UUID mismatch") - testutils.AssertEqual(t, b2.UUID, "b2-uuid", "b2 UUID mismatch") + assert.Equal(t, b1.Dirty, false, "b1 Dirty mismatch") + assert.Equal(t, b2.Dirty, false, "b2 Dirty mismatch") + assert.Equal(t, b3.Dirty, false, "b3 Dirty mismatch") + assert.Equal(t, b4.Dirty, false, "b4 Dirty mismatch") + assert.Equal(t, b7.Dirty, false, "b7 Dirty mismatch") + assert.Equal(t, b8.Dirty, false, "b8 Dirty mismatch") + assert.Equal(t, b1.UUID, "b1-uuid", "b1 UUID mismatch") + assert.Equal(t, b2.UUID, "b2-uuid", "b2 UUID mismatch") // uuids of created books should have been updated - testutils.AssertEqual(t, b3.UUID, "server-b3-label-uuid", "b3 UUID mismatch") - testutils.AssertEqual(t, b4.UUID, "server-b4-label-uuid", "b4 UUID mismatch") - testutils.AssertEqual(t, b7.UUID, "b7-uuid", "b7 UUID mismatch") - testutils.AssertEqual(t, b8.UUID, "b8-uuid", "b8 UUID mismatch") + assert.Equal(t, b3.UUID, "server-b3-label-uuid", "b3 UUID mismatch") + assert.Equal(t, b4.UUID, "server-b4-label-uuid", "b4 UUID mismatch") + assert.Equal(t, b7.UUID, "b7-uuid", "b7 UUID mismatch") + assert.Equal(t, b8.UUID, "b8-uuid", "b8 UUID mismatch") - var n1, n2, n3, n4, n5, n6, n7 core.Note - testutils.MustScan(t, "getting n1", db.QueryRow("SELECT book_uuid FROM notes WHERE body = ?", "n1 body"), &n1.BookUUID) - testutils.MustScan(t, "getting n2", db.QueryRow("SELECT book_uuid FROM notes WHERE body = ?", "n2 body"), &n2.BookUUID) - testutils.MustScan(t, "getting n3", db.QueryRow("SELECT book_uuid FROM notes WHERE body = ?", "n3 body"), &n3.BookUUID) - testutils.MustScan(t, "getting n4", db.QueryRow("SELECT book_uuid FROM notes WHERE body = ?", "n4 body"), &n4.BookUUID) - testutils.MustScan(t, "getting n5", db.QueryRow("SELECT book_uuid FROM notes WHERE body = ?", "n5 body"), &n5.BookUUID) - testutils.MustScan(t, "getting n6", db.QueryRow("SELECT book_uuid FROM notes WHERE body = ?", "n6 body"), &n6.BookUUID) - testutils.MustScan(t, "getting n7", db.QueryRow("SELECT book_uuid FROM notes WHERE body = ?", "n7 body"), &n7.BookUUID) - testutils.AssertEqual(t, n1.BookUUID, "b1-uuid", "n1 bookUUID mismatch") - testutils.AssertEqual(t, n2.BookUUID, "b5-uuid", "n2 bookUUID mismatch") - testutils.AssertEqual(t, n3.BookUUID, "b6-uuid", "n3 bookUUID mismatch") - testutils.AssertEqual(t, n4.BookUUID, "b7-uuid", "n4 bookUUID mismatch") - testutils.AssertEqual(t, n5.BookUUID, "server-b3-label-uuid", "n5 bookUUID mismatch") - testutils.AssertEqual(t, n6.BookUUID, "server-b3-label-uuid", "n6 bookUUID mismatch") - testutils.AssertEqual(t, n7.BookUUID, "server-b4-label-uuid", "n7 bookUUID mismatch") + var n1, n2, n3, n4, n5, n6, n7 database.Note + database.MustScan(t, "getting n1", db.QueryRow("SELECT book_uuid FROM notes WHERE body = ?", "n1 body"), &n1.BookUUID) + database.MustScan(t, "getting n2", db.QueryRow("SELECT book_uuid FROM notes WHERE body = ?", "n2 body"), &n2.BookUUID) + database.MustScan(t, "getting n3", db.QueryRow("SELECT book_uuid FROM notes WHERE body = ?", "n3 body"), &n3.BookUUID) + database.MustScan(t, "getting n4", db.QueryRow("SELECT book_uuid FROM notes WHERE body = ?", "n4 body"), &n4.BookUUID) + database.MustScan(t, "getting n5", db.QueryRow("SELECT book_uuid FROM notes WHERE body = ?", "n5 body"), &n5.BookUUID) + database.MustScan(t, "getting n6", db.QueryRow("SELECT book_uuid FROM notes WHERE body = ?", "n6 body"), &n6.BookUUID) + database.MustScan(t, "getting n7", db.QueryRow("SELECT book_uuid FROM notes WHERE body = ?", "n7 body"), &n7.BookUUID) + assert.Equal(t, n1.BookUUID, "b1-uuid", "n1 bookUUID mismatch") + assert.Equal(t, n2.BookUUID, "b5-uuid", "n2 bookUUID mismatch") + assert.Equal(t, n3.BookUUID, "b6-uuid", "n3 bookUUID mismatch") + assert.Equal(t, n4.BookUUID, "b7-uuid", "n4 bookUUID mismatch") + assert.Equal(t, n5.BookUUID, "server-b3-label-uuid", "n5 bookUUID mismatch") + assert.Equal(t, n6.BookUUID, "server-b3-label-uuid", "n6 bookUUID mismatch") + assert.Equal(t, n7.BookUUID, "server-b4-label-uuid", "n7 bookUUID mismatch") } func TestSendBooks_isBehind(t *testing.T) { @@ -2159,15 +2109,15 @@ func TestSendBooks_isBehind(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - testutils.Login(t, &ctx) + ctx := context.InitTestCtx(t, "../../tmp", nil) ctx.APIEndpoint = ts.URL - defer testutils.TeardownEnv(ctx) + defer context.TeardownTestCtx(t, ctx) + testutils.Login(t, &ctx) db := ctx.DB - testutils.MustExec(t, fmt.Sprintf("inserting last max usn for test case %d", idx), db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemLastMaxUSN, tc.systemLastMaxUSN) - testutils.MustExec(t, fmt.Sprintf("inserting b1 for test case %d", idx), db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 0, false, true) + database.MustExec(t, fmt.Sprintf("inserting last max usn for test case %d", idx), db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemLastMaxUSN, tc.systemLastMaxUSN) + database.MustExec(t, fmt.Sprintf("inserting b1 for test case %d", idx), db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 0, false, true) // execute tx, err := db.Begin() @@ -2184,7 +2134,7 @@ func TestSendBooks_isBehind(t *testing.T) { tx.Commit() // test - testutils.AssertEqual(t, isBehind, tc.expectedIsBehind, fmt.Sprintf("isBehind mismatch for test case %d", idx)) + assert.Equal(t, isBehind, tc.expectedIsBehind, fmt.Sprintf("isBehind mismatch for test case %d", idx)) }() } }) @@ -2207,15 +2157,15 @@ func TestSendBooks_isBehind(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - testutils.Login(t, &ctx) + ctx := context.InitTestCtx(t, "../../tmp", nil) ctx.APIEndpoint = ts.URL - defer testutils.TeardownEnv(ctx) + defer context.TeardownTestCtx(t, ctx) + testutils.Login(t, &ctx) db := ctx.DB - testutils.MustExec(t, fmt.Sprintf("inserting last max usn for test case %d", idx), db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemLastMaxUSN, tc.systemLastMaxUSN) - testutils.MustExec(t, fmt.Sprintf("inserting b1 for test case %d", idx), db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 1, true, true) + database.MustExec(t, fmt.Sprintf("inserting last max usn for test case %d", idx), db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemLastMaxUSN, tc.systemLastMaxUSN) + database.MustExec(t, fmt.Sprintf("inserting b1 for test case %d", idx), db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 1, true, true) // execute tx, err := db.Begin() @@ -2232,7 +2182,7 @@ func TestSendBooks_isBehind(t *testing.T) { tx.Commit() // test - testutils.AssertEqual(t, isBehind, tc.expectedIsBehind, fmt.Sprintf("isBehind mismatch for test case %d", idx)) + assert.Equal(t, isBehind, tc.expectedIsBehind, fmt.Sprintf("isBehind mismatch for test case %d", idx)) }() } }) @@ -2255,15 +2205,15 @@ func TestSendBooks_isBehind(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - testutils.Login(t, &ctx) + ctx := context.InitTestCtx(t, "../../tmp", nil) ctx.APIEndpoint = ts.URL - defer testutils.TeardownEnv(ctx) + defer context.TeardownTestCtx(t, ctx) + testutils.Login(t, &ctx) db := ctx.DB - testutils.MustExec(t, fmt.Sprintf("inserting last max usn for test case %d", idx), db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemLastMaxUSN, tc.systemLastMaxUSN) - testutils.MustExec(t, fmt.Sprintf("inserting b1 for test case %d", idx), db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 11, false, true) + database.MustExec(t, fmt.Sprintf("inserting last max usn for test case %d", idx), db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemLastMaxUSN, tc.systemLastMaxUSN) + database.MustExec(t, fmt.Sprintf("inserting b1 for test case %d", idx), db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 11, false, true) // execute tx, err := db.Begin() @@ -2280,7 +2230,7 @@ func TestSendBooks_isBehind(t *testing.T) { tx.Commit() // test - testutils.AssertEqual(t, isBehind, tc.expectedIsBehind, fmt.Sprintf("isBehind mismatch for test case %d", idx)) + assert.Equal(t, isBehind, tc.expectedIsBehind, fmt.Sprintf("isBehind mismatch for test case %d", idx)) }() } }) @@ -2290,37 +2240,37 @@ func TestSendBooks_isBehind(t *testing.T) { // uuid from the incoming data. func TestSendNotes(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) + ctx := context.InitTestCtx(t, "../../tmp", nil) + defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) - defer testutils.TeardownEnv(ctx) db := ctx.DB - testutils.MustExec(t, "inserting last max usn", db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemLastMaxUSN, 0) + database.MustExec(t, "inserting last max usn", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemLastMaxUSN, 0) b1UUID := "b1-uuid" - testutils.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b1UUID, "b1-label", 1, false, false) + database.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b1UUID, "b1-label", 1, false, false) // should be ignored - testutils.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 10, "n1-body", 1541108743, false, false) + database.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 10, "n1-body", 1541108743, false, false) // should be created - testutils.MustExec(t, "inserting n2", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n2-uuid", b1UUID, 0, "n2-body", 1541108743, false, true) + database.MustExec(t, "inserting n2", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n2-uuid", b1UUID, 0, "n2-body", 1541108743, false, true) // should be updated - testutils.MustExec(t, "inserting n3", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n3-uuid", b1UUID, 11, "n3-body", 1541108743, false, true) + database.MustExec(t, "inserting n3", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n3-uuid", b1UUID, 11, "n3-body", 1541108743, false, true) // should be only expunged locally without syncing to server - testutils.MustExec(t, "inserting n4", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n4-uuid", b1UUID, 0, "n4-body", 1541108743, true, true) + database.MustExec(t, "inserting n4", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n4-uuid", b1UUID, 0, "n4-body", 1541108743, true, true) // should be deleted - testutils.MustExec(t, "inserting n5", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n5-uuid", b1UUID, 17, "n5-body", 1541108743, true, true) + database.MustExec(t, "inserting n5", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n5-uuid", b1UUID, 17, "n5-body", 1541108743, true, true) // should be created - testutils.MustExec(t, "inserting n6", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n6-uuid", b1UUID, 0, "n6-body", 1541108743, false, true) + database.MustExec(t, "inserting n6", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n6-uuid", b1UUID, 0, "n6-body", 1541108743, false, true) // should be ignored - testutils.MustExec(t, "inserting n7", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n7-uuid", b1UUID, 12, "n7-body", 1541108743, false, false) + database.MustExec(t, "inserting n7", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n7-uuid", b1UUID, 12, "n7-body", 1541108743, false, false) // should be updated - testutils.MustExec(t, "inserting n8", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n8-uuid", b1UUID, 17, "n8-body", 1541108743, false, true) + database.MustExec(t, "inserting n8", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n8-uuid", b1UUID, 17, "n8-body", 1541108743, false, true) // should be deleted - testutils.MustExec(t, "inserting n9", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n9-uuid", b1UUID, 17, "n9-body", 1541108743, true, true) + database.MustExec(t, "inserting n9", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n9-uuid", b1UUID, 17, "n9-body", 1541108743, true, true) // should be created - testutils.MustExec(t, "inserting n10", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n10-uuid", b1UUID, 0, "n10-body", 1541108743, false, true) + database.MustExec(t, "inserting n10", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n10-uuid", b1UUID, 0, "n10-body", 1541108743, false, true) var createdBodys []string var updatedUUIDs []string @@ -2402,64 +2352,64 @@ func TestSendNotes(t *testing.T) { return strings.Compare(createdBodys[i], createdBodys[j]) < 0 }) - testutils.AssertDeepEqual(t, createdBodys, []string{"n10-body", "n2-body", "n6-body"}, "createdBodys mismatch") - testutils.AssertDeepEqual(t, updatedUUIDs, []string{"n3-uuid", "n8-uuid"}, "updatedUUIDs mismatch") - testutils.AssertDeepEqual(t, deletedUUIDs, []string{"n5-uuid", "n9-uuid"}, "deletedUUIDs mismatch") + assert.DeepEqual(t, createdBodys, []string{"n10-body", "n2-body", "n6-body"}, "createdBodys mismatch") + assert.DeepEqual(t, updatedUUIDs, []string{"n3-uuid", "n8-uuid"}, "updatedUUIDs mismatch") + assert.DeepEqual(t, deletedUUIDs, []string{"n5-uuid", "n9-uuid"}, "deletedUUIDs mismatch") var noteCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.AssertEqualf(t, noteCount, 7, "note count mismatch") + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + assert.Equalf(t, noteCount, 7, "note count mismatch") - var n1, n2, n3, n6, n7, n8, n10 core.Note - testutils.MustScan(t, "getting n1", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n1-body"), &n1.UUID, &n1.AddedOn, &n1.Dirty) - testutils.MustScan(t, "getting n2", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n2-body"), &n2.UUID, &n2.AddedOn, &n2.Dirty) - testutils.MustScan(t, "getting n3", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n3-body"), &n3.UUID, &n3.AddedOn, &n3.Dirty) - testutils.MustScan(t, "getting n6", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n6-body"), &n6.UUID, &n6.AddedOn, &n6.Dirty) - testutils.MustScan(t, "getting n7", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n7-body"), &n7.UUID, &n7.AddedOn, &n7.Dirty) - testutils.MustScan(t, "getting n8", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n8-body"), &n8.UUID, &n8.AddedOn, &n8.Dirty) - testutils.MustScan(t, "getting n10", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n10-body"), &n10.UUID, &n10.AddedOn, &n10.Dirty) + var n1, n2, n3, n6, n7, n8, n10 database.Note + database.MustScan(t, "getting n1", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n1-body"), &n1.UUID, &n1.AddedOn, &n1.Dirty) + database.MustScan(t, "getting n2", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n2-body"), &n2.UUID, &n2.AddedOn, &n2.Dirty) + database.MustScan(t, "getting n3", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n3-body"), &n3.UUID, &n3.AddedOn, &n3.Dirty) + database.MustScan(t, "getting n6", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n6-body"), &n6.UUID, &n6.AddedOn, &n6.Dirty) + database.MustScan(t, "getting n7", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n7-body"), &n7.UUID, &n7.AddedOn, &n7.Dirty) + database.MustScan(t, "getting n8", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n8-body"), &n8.UUID, &n8.AddedOn, &n8.Dirty) + database.MustScan(t, "getting n10", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n10-body"), &n10.UUID, &n10.AddedOn, &n10.Dirty) - testutils.AssertEqualf(t, noteCount, 7, "note count mismatch") + assert.Equalf(t, noteCount, 7, "note count mismatch") - testutils.AssertEqual(t, n1.Dirty, false, "n1 Dirty mismatch") - testutils.AssertEqual(t, n2.Dirty, false, "n2 Dirty mismatch") - testutils.AssertEqual(t, n3.Dirty, false, "n3 Dirty mismatch") - testutils.AssertEqual(t, n6.Dirty, false, "n6 Dirty mismatch") - testutils.AssertEqual(t, n7.Dirty, false, "n7 Dirty mismatch") - testutils.AssertEqual(t, n8.Dirty, false, "n8 Dirty mismatch") - testutils.AssertEqual(t, n10.Dirty, false, "n10 Dirty mismatch") + assert.Equal(t, n1.Dirty, false, "n1 Dirty mismatch") + assert.Equal(t, n2.Dirty, false, "n2 Dirty mismatch") + assert.Equal(t, n3.Dirty, false, "n3 Dirty mismatch") + assert.Equal(t, n6.Dirty, false, "n6 Dirty mismatch") + assert.Equal(t, n7.Dirty, false, "n7 Dirty mismatch") + assert.Equal(t, n8.Dirty, false, "n8 Dirty mismatch") + assert.Equal(t, n10.Dirty, false, "n10 Dirty mismatch") - testutils.AssertEqual(t, n1.AddedOn, int64(1541108743), "n1 AddedOn mismatch") - testutils.AssertEqual(t, n2.AddedOn, int64(1541108743), "n2 AddedOn mismatch") - testutils.AssertEqual(t, n3.AddedOn, int64(1541108743), "n3 AddedOn mismatch") - testutils.AssertEqual(t, n6.AddedOn, int64(1541108743), "n6 AddedOn mismatch") - testutils.AssertEqual(t, n7.AddedOn, int64(1541108743), "n7 AddedOn mismatch") - testutils.AssertEqual(t, n8.AddedOn, int64(1541108743), "n8 AddedOn mismatch") - testutils.AssertEqual(t, n10.AddedOn, int64(1541108743), "n10 AddedOn mismatch") + assert.Equal(t, n1.AddedOn, int64(1541108743), "n1 AddedOn mismatch") + assert.Equal(t, n2.AddedOn, int64(1541108743), "n2 AddedOn mismatch") + assert.Equal(t, n3.AddedOn, int64(1541108743), "n3 AddedOn mismatch") + assert.Equal(t, n6.AddedOn, int64(1541108743), "n6 AddedOn mismatch") + assert.Equal(t, n7.AddedOn, int64(1541108743), "n7 AddedOn mismatch") + assert.Equal(t, n8.AddedOn, int64(1541108743), "n8 AddedOn mismatch") + assert.Equal(t, n10.AddedOn, int64(1541108743), "n10 AddedOn mismatch") // UUIDs of created notes should have been updated with those from the server response - testutils.AssertEqual(t, n1.UUID, "n1-uuid", "n1 UUID mismatch") - testutils.AssertEqual(t, n2.UUID, "server-n2-body-uuid", "n2 UUID mismatch") - testutils.AssertEqual(t, n3.UUID, "n3-uuid", "n3 UUID mismatch") - testutils.AssertEqual(t, n6.UUID, "server-n6-body-uuid", "n6 UUID mismatch") - testutils.AssertEqual(t, n7.UUID, "n7-uuid", "n7 UUID mismatch") - testutils.AssertEqual(t, n8.UUID, "n8-uuid", "n8 UUID mismatch") - testutils.AssertEqual(t, n10.UUID, "server-n10-body-uuid", "n10 UUID mismatch") + assert.Equal(t, n1.UUID, "n1-uuid", "n1 UUID mismatch") + assert.Equal(t, n2.UUID, "server-n2-body-uuid", "n2 UUID mismatch") + assert.Equal(t, n3.UUID, "n3-uuid", "n3 UUID mismatch") + assert.Equal(t, n6.UUID, "server-n6-body-uuid", "n6 UUID mismatch") + assert.Equal(t, n7.UUID, "n7-uuid", "n7 UUID mismatch") + assert.Equal(t, n8.UUID, "n8-uuid", "n8 UUID mismatch") + assert.Equal(t, n10.UUID, "server-n10-body-uuid", "n10 UUID mismatch") } func TestSendNotes_addedOn(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) + ctx := context.InitTestCtx(t, "../../tmp", nil) + defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) - defer testutils.TeardownEnv(ctx) db := ctx.DB - testutils.MustExec(t, "inserting last max usn", db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemLastMaxUSN, 0) + database.MustExec(t, "inserting last max usn", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemLastMaxUSN, 0) // should be created b1UUID := "b1-uuid" - testutils.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 0, "n1-body", 1541108743, false, true) + database.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 0, "n1-body", 1541108743, false, true) // fire up a test server. It decrypts the payload for test purposes. ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -2498,9 +2448,9 @@ func TestSendNotes_addedOn(t *testing.T) { tx.Commit() // test - var n1 core.Note - testutils.MustScan(t, "getting n1", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n1-body"), &n1.UUID, &n1.AddedOn, &n1.Dirty) - testutils.AssertEqual(t, n1.AddedOn, int64(1541108743), "n1 AddedOn mismatch") + var n1 database.Note + database.MustScan(t, "getting n1", db.QueryRow("SELECT uuid, added_on, dirty FROM notes WHERE body = ?", "n1-body"), &n1.UUID, &n1.AddedOn, &n1.Dirty) + assert.Equal(t, n1.AddedOn, int64(1541108743), "n1 AddedOn mismatch") } func TestSendNotes_isBehind(t *testing.T) { @@ -2581,16 +2531,16 @@ func TestSendNotes_isBehind(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) + ctx := context.InitTestCtx(t, "../../tmp", nil) + defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) ctx.APIEndpoint = ts.URL - defer testutils.TeardownEnv(ctx) db := ctx.DB - testutils.MustExec(t, fmt.Sprintf("inserting last max usn for test case %d", idx), db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemLastMaxUSN, tc.systemLastMaxUSN) - testutils.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 1, false, false) - testutils.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", "b1-uuid", 1, "n1 body", 1541108743, false, true) + database.MustExec(t, fmt.Sprintf("inserting last max usn for test case %d", idx), db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemLastMaxUSN, tc.systemLastMaxUSN) + database.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 1, false, false) + database.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", "b1-uuid", 1, "n1 body", 1541108743, false, true) // execute tx, err := db.Begin() @@ -2607,7 +2557,7 @@ func TestSendNotes_isBehind(t *testing.T) { tx.Commit() // test - testutils.AssertEqual(t, isBehind, tc.expectedIsBehind, fmt.Sprintf("isBehind mismatch for test case %d", idx)) + assert.Equal(t, isBehind, tc.expectedIsBehind, fmt.Sprintf("isBehind mismatch for test case %d", idx)) }() } }) @@ -2630,16 +2580,16 @@ func TestSendNotes_isBehind(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) + ctx := context.InitTestCtx(t, "../../tmp", nil) + defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) ctx.APIEndpoint = ts.URL - defer testutils.TeardownEnv(ctx) db := ctx.DB - testutils.MustExec(t, fmt.Sprintf("inserting last max usn for test case %d", idx), db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemLastMaxUSN, tc.systemLastMaxUSN) - testutils.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 1, false, false) - testutils.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", "b1-uuid", 2, "n1 body", 1541108743, true, true) + database.MustExec(t, fmt.Sprintf("inserting last max usn for test case %d", idx), db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemLastMaxUSN, tc.systemLastMaxUSN) + database.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 1, false, false) + database.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", "b1-uuid", 2, "n1 body", 1541108743, true, true) // execute tx, err := db.Begin() @@ -2656,7 +2606,7 @@ func TestSendNotes_isBehind(t *testing.T) { tx.Commit() // test - testutils.AssertEqual(t, isBehind, tc.expectedIsBehind, fmt.Sprintf("isBehind mismatch for test case %d", idx)) + assert.Equal(t, isBehind, tc.expectedIsBehind, fmt.Sprintf("isBehind mismatch for test case %d", idx)) }() } }) @@ -2679,16 +2629,16 @@ func TestSendNotes_isBehind(t *testing.T) { for idx, tc := range testCases { func() { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) + ctx := context.InitTestCtx(t, "../../tmp", nil) + defer context.TeardownTestCtx(t, ctx) testutils.Login(t, &ctx) ctx.APIEndpoint = ts.URL - defer testutils.TeardownEnv(ctx) db := ctx.DB - testutils.MustExec(t, fmt.Sprintf("inserting last max usn for test case %d", idx), db, "INSERT INTO system (key, value) VALUES (?, ?)", infra.SystemLastMaxUSN, tc.systemLastMaxUSN) - testutils.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 1, false, false) - testutils.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", "b1-uuid", 8, "n1 body", 1541108743, false, true) + database.MustExec(t, fmt.Sprintf("inserting last max usn for test case %d", idx), db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemLastMaxUSN, tc.systemLastMaxUSN) + database.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 1, false, false) + database.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", "b1-uuid", 8, "n1 body", 1541108743, false, true) // execute tx, err := db.Begin() @@ -2705,7 +2655,7 @@ func TestSendNotes_isBehind(t *testing.T) { tx.Commit() // test - testutils.AssertEqual(t, isBehind, tc.expectedIsBehind, fmt.Sprintf("isBehind mismatch for test case %d", idx)) + assert.Equal(t, isBehind, tc.expectedIsBehind, fmt.Sprintf("isBehind mismatch for test case %d", idx)) }() } }) @@ -2845,16 +2795,14 @@ n1 body edited for idx, tc := range testCases { func() { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, "../../tmp/.dnote", nil) + defer database.CloseTestDB(t, db) - db := ctx.DB - - testutils.MustExec(t, fmt.Sprintf("inserting b1 for test case %d", idx), db, "INSERT INTO books (uuid, label, usn, dirty) VALUES (?, ?, ?, ?)", b1UUID, "b1-label", 5, false) - testutils.MustExec(t, fmt.Sprintf("inserting b2 for test case %d", idx), db, "INSERT INTO books (uuid, label, usn, dirty) VALUES (?, ?, ?, ?)", b2UUID, "b2-label", 6, false) - testutils.MustExec(t, fmt.Sprintf("inserting conflitcs book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", conflictBookUUID, "conflicts") + database.MustExec(t, fmt.Sprintf("inserting b1 for test case %d", idx), db, "INSERT INTO books (uuid, label, usn, dirty) VALUES (?, ?, ?, ?)", b1UUID, "b1-label", 5, false) + database.MustExec(t, fmt.Sprintf("inserting b2 for test case %d", idx), db, "INSERT INTO books (uuid, label, usn, dirty) VALUES (?, ?, ?, ?)", b2UUID, "b2-label", 6, false) + database.MustExec(t, fmt.Sprintf("inserting conflitcs book for test case %d", idx), db, "INSERT INTO books (uuid, label) VALUES (?, ?)", conflictBookUUID, "conflicts") n1UUID := utils.GenerateUUID() - testutils.MustExec(t, fmt.Sprintf("inserting n1 for test case %d", idx), db, "INSERT INTO notes (uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", n1UUID, b1UUID, tc.clientUSN, tc.addedOn, tc.clientEditedOn, tc.clientBody, tc.clientDeleted, tc.clientDirty) + database.MustExec(t, fmt.Sprintf("inserting n1 for test case %d", idx), db, "INSERT INTO notes (uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", n1UUID, b1UUID, tc.clientUSN, tc.addedOn, tc.clientEditedOn, tc.clientBody, tc.clientDeleted, tc.clientDirty) // execute tx, err := db.Begin() @@ -2872,8 +2820,8 @@ n1 body edited Body: tc.serverBody, Deleted: tc.serverDeleted, } - var localNote core.Note - testutils.MustScan(t, fmt.Sprintf("getting localNote for test case %d", idx), + var localNote database.Note + database.MustScan(t, fmt.Sprintf("getting localNote for test case %d", idx), db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", n1UUID), &localNote.UUID, &localNote.BookUUID, &localNote.USN, &localNote.AddedOn, &localNote.EditedOn, &localNote.Body, &localNote.Deleted, &localNote.Dirty) @@ -2886,61 +2834,59 @@ n1 body edited // test var noteCount, bookCount int - testutils.MustScan(t, fmt.Sprintf("counting notes for test case %d", idx), db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, fmt.Sprintf("counting books for test case %d", idx), db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, fmt.Sprintf("counting notes for test case %d", idx), db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, fmt.Sprintf("counting books for test case %d", idx), db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, noteCount, 1, fmt.Sprintf("note count mismatch for test case %d", idx)) - testutils.AssertEqualf(t, bookCount, 3, fmt.Sprintf("book count mismatch for test case %d", idx)) + assert.Equalf(t, noteCount, 1, fmt.Sprintf("note count mismatch for test case %d", idx)) + assert.Equalf(t, bookCount, 3, fmt.Sprintf("book count mismatch for test case %d", idx)) - var n1Record core.Note - testutils.MustScan(t, fmt.Sprintf("getting n1Record for test case %d", idx), + var n1Record database.Note + database.MustScan(t, fmt.Sprintf("getting n1Record for test case %d", idx), db.QueryRow("SELECT uuid, book_uuid, usn, added_on, edited_on, body, deleted, dirty FROM notes WHERE uuid = ?", n1UUID), &n1Record.UUID, &n1Record.BookUUID, &n1Record.USN, &n1Record.AddedOn, &n1Record.EditedOn, &n1Record.Body, &n1Record.Deleted, &n1Record.Dirty) - var b1Record core.Book - testutils.MustScan(t, "getting b1Record for test case", + var b1Record database.Book + database.MustScan(t, "getting b1Record for test case", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", b1UUID), &b1Record.UUID, &b1Record.Label, &b1Record.USN, &b1Record.Dirty) - var b2Record core.Book - testutils.MustScan(t, "getting b2Record for test case", + var b2Record database.Book + database.MustScan(t, "getting b2Record for test case", db.QueryRow("SELECT uuid, label, usn, dirty FROM books WHERE uuid = ?", b2UUID), &b2Record.UUID, &b2Record.Label, &b2Record.USN, &b2Record.Dirty) - testutils.AssertEqual(t, b1Record.UUID, b1UUID, fmt.Sprintf("b1Record UUID mismatch for test case %d", idx)) - testutils.AssertEqual(t, b1Record.Label, "b1-label", fmt.Sprintf("b1Record Label mismatch for test case %d", idx)) - testutils.AssertEqual(t, b1Record.USN, 5, fmt.Sprintf("b1Record USN mismatch for test case %d", idx)) - testutils.AssertEqual(t, b1Record.Dirty, false, fmt.Sprintf("b1Record Dirty mismatch for test case %d", idx)) + assert.Equal(t, b1Record.UUID, b1UUID, fmt.Sprintf("b1Record UUID mismatch for test case %d", idx)) + assert.Equal(t, b1Record.Label, "b1-label", fmt.Sprintf("b1Record Label mismatch for test case %d", idx)) + assert.Equal(t, b1Record.USN, 5, fmt.Sprintf("b1Record USN mismatch for test case %d", idx)) + assert.Equal(t, b1Record.Dirty, false, fmt.Sprintf("b1Record Dirty mismatch for test case %d", idx)) - testutils.AssertEqual(t, b2Record.UUID, b2UUID, fmt.Sprintf("b2Record UUID mismatch for test case %d", idx)) - testutils.AssertEqual(t, b2Record.Label, "b2-label", fmt.Sprintf("b2Record Label mismatch for test case %d", idx)) - testutils.AssertEqual(t, b2Record.USN, 6, fmt.Sprintf("b2Record USN mismatch for test case %d", idx)) - testutils.AssertEqual(t, b2Record.Dirty, false, fmt.Sprintf("b2Record Dirty mismatch for test case %d", idx)) + assert.Equal(t, b2Record.UUID, b2UUID, fmt.Sprintf("b2Record UUID mismatch for test case %d", idx)) + assert.Equal(t, b2Record.Label, "b2-label", fmt.Sprintf("b2Record Label mismatch for test case %d", idx)) + assert.Equal(t, b2Record.USN, 6, fmt.Sprintf("b2Record USN mismatch for test case %d", idx)) + assert.Equal(t, b2Record.Dirty, false, fmt.Sprintf("b2Record Dirty mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.UUID, n1UUID, fmt.Sprintf("n1Record UUID mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.BookUUID, tc.expectedBookUUID, fmt.Sprintf("n1Record BookUUID mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.USN, tc.expectedUSN, fmt.Sprintf("n1Record USN mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.AddedOn, tc.expectedAddedOn, fmt.Sprintf("n1Record AddedOn mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.EditedOn, tc.expectedEditedOn, fmt.Sprintf("n1Record EditedOn mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.Body, tc.expectedBody, fmt.Sprintf("n1Record Body mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.Deleted, tc.expectedDeleted, fmt.Sprintf("n1Record Deleted mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.Dirty, tc.expectedDirty, fmt.Sprintf("n1Record Dirty mismatch for test case %d", idx)) + assert.Equal(t, n1Record.UUID, n1UUID, fmt.Sprintf("n1Record UUID mismatch for test case %d", idx)) + assert.Equal(t, n1Record.BookUUID, tc.expectedBookUUID, fmt.Sprintf("n1Record BookUUID mismatch for test case %d", idx)) + assert.Equal(t, n1Record.USN, tc.expectedUSN, fmt.Sprintf("n1Record USN mismatch for test case %d", idx)) + assert.Equal(t, n1Record.AddedOn, tc.expectedAddedOn, fmt.Sprintf("n1Record AddedOn mismatch for test case %d", idx)) + assert.Equal(t, n1Record.EditedOn, tc.expectedEditedOn, fmt.Sprintf("n1Record EditedOn mismatch for test case %d", idx)) + assert.Equal(t, n1Record.Body, tc.expectedBody, fmt.Sprintf("n1Record Body mismatch for test case %d", idx)) + assert.Equal(t, n1Record.Deleted, tc.expectedDeleted, fmt.Sprintf("n1Record Deleted mismatch for test case %d", idx)) + assert.Equal(t, n1Record.Dirty, tc.expectedDirty, fmt.Sprintf("n1Record Dirty mismatch for test case %d", idx)) }() } } func TestCheckBookPristine(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, "../../tmp/.dnote", nil) + defer database.CloseTestDB(t, db) - db := ctx.DB - - testutils.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, dirty) VALUES (?, ?, ?, ?)", "b1-uuid", "b1-label", 5, false) - testutils.MustExec(t, "inserting b2", db, "INSERT INTO books (uuid, label, usn, dirty) VALUES (?, ?, ?, ?)", "b2-uuid", "b2-label", 6, false) - testutils.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, added_on, body, dirty) VALUES (?, ?, ?, ?, ?)", "n1-uuid", "b1-uuid", 1541108743, "n1 body", false) - testutils.MustExec(t, "inserting n2", db, "INSERT INTO notes (uuid, book_uuid, added_on, body, dirty) VALUES (?, ?, ?, ?, ?)", "n2-uuid", "b1-uuid", 1541108743, "n2 body", false) - testutils.MustExec(t, "inserting n3", db, "INSERT INTO notes (uuid, book_uuid, added_on, body, dirty) VALUES (?, ?, ?, ?, ?)", "n3-uuid", "b1-uuid", 1541108743, "n3 body", true) - testutils.MustExec(t, "inserting n4", db, "INSERT INTO notes (uuid, book_uuid, added_on, body, dirty) VALUES (?, ?, ?, ?, ?)", "n4-uuid", "b2-uuid", 1541108743, "n4 body", false) - testutils.MustExec(t, "inserting n5", db, "INSERT INTO notes (uuid, book_uuid, added_on, body, dirty) VALUES (?, ?, ?, ?, ?)", "n5-uuid", "b2-uuid", 1541108743, "n5 body", false) + database.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, dirty) VALUES (?, ?, ?, ?)", "b1-uuid", "b1-label", 5, false) + database.MustExec(t, "inserting b2", db, "INSERT INTO books (uuid, label, usn, dirty) VALUES (?, ?, ?, ?)", "b2-uuid", "b2-label", 6, false) + database.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, added_on, body, dirty) VALUES (?, ?, ?, ?, ?)", "n1-uuid", "b1-uuid", 1541108743, "n1 body", false) + database.MustExec(t, "inserting n2", db, "INSERT INTO notes (uuid, book_uuid, added_on, body, dirty) VALUES (?, ?, ?, ?, ?)", "n2-uuid", "b1-uuid", 1541108743, "n2 body", false) + database.MustExec(t, "inserting n3", db, "INSERT INTO notes (uuid, book_uuid, added_on, body, dirty) VALUES (?, ?, ?, ?, ?)", "n3-uuid", "b1-uuid", 1541108743, "n3 body", true) + database.MustExec(t, "inserting n4", db, "INSERT INTO notes (uuid, book_uuid, added_on, body, dirty) VALUES (?, ?, ?, ?, ?)", "n4-uuid", "b2-uuid", 1541108743, "n4 body", false) + database.MustExec(t, "inserting n5", db, "INSERT INTO notes (uuid, book_uuid, added_on, body, dirty) VALUES (?, ?, ?, ?, ?)", "n5-uuid", "b2-uuid", 1541108743, "n5 body", false) t.Run("b1", func(t *testing.T) { // execute @@ -2957,7 +2903,7 @@ func TestCheckBookPristine(t *testing.T) { tx.Commit() // test - testutils.AssertEqual(t, got, false, "b1 should not be pristine") + assert.Equal(t, got, false, "b1 should not be pristine") }) t.Run("b2", func(t *testing.T) { @@ -2975,7 +2921,7 @@ func TestCheckBookPristine(t *testing.T) { tx.Commit() // test - testutils.AssertEqual(t, got, true, "b2 should be pristine") + assert.Equal(t, got, true, "b2 should be pristine") }) } @@ -3037,7 +2983,7 @@ func TestCheckNoteInList(t *testing.T) { for idx, tc := range testCases { got := checkNoteInList(tc.uuid, &list) - testutils.AssertEqual(t, got, tc.expected, fmt.Sprintf("result mismatch for test case %d", idx)) + assert.Equal(t, got, tc.expected, fmt.Sprintf("result mismatch for test case %d", idx)) } } @@ -3099,16 +3045,14 @@ func TestCheckBookInList(t *testing.T) { for idx, tc := range testCases { got := checkBookInList(tc.uuid, &list) - testutils.AssertEqual(t, got, tc.expected, fmt.Sprintf("result mismatch for test case %d", idx)) + assert.Equal(t, got, tc.expected, fmt.Sprintf("result mismatch for test case %d", idx)) } } func TestCleanLocalNotes(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB + db := database.InitTestDB(t, "../../tmp/.dnote", nil) + defer database.CloseTestDB(t, db) list := syncList{ Notes: map[string]client.SyncFragNote{ @@ -3140,18 +3084,18 @@ func TestCleanLocalNotes(t *testing.T) { } b1UUID := "b1-uuid" - testutils.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b1UUID, "b1-label", 1, false, false) + database.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b1UUID, "b1-label", 1, false, false) // exists in the list - testutils.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 10, "n1 body", 1541108743, false, false) - testutils.MustExec(t, "inserting n2", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n2-uuid", b1UUID, 0, "n2 body", 1541108743, false, true) + database.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n1-uuid", b1UUID, 10, "n1 body", 1541108743, false, false) + database.MustExec(t, "inserting n2", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n2-uuid", b1UUID, 0, "n2 body", 1541108743, false, true) // non-existent in the list but in valid state // (created in the cli and hasn't been uploaded) - testutils.MustExec(t, "inserting n6", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n6-uuid", b1UUID, 0, "n6 body", 1541108743, false, true) + database.MustExec(t, "inserting n6", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n6-uuid", b1UUID, 0, "n6 body", 1541108743, false, true) // non-existent in the list and in an invalid state - testutils.MustExec(t, "inserting n5", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n5-uuid", b1UUID, 7, "n5 body", 1541108743, true, true) - testutils.MustExec(t, "inserting n9", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n9-uuid", b1UUID, 17, "n9 body", 1541108743, true, false) - testutils.MustExec(t, "inserting n10", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n10-uuid", b1UUID, 0, "n10 body", 1541108743, false, false) + database.MustExec(t, "inserting n5", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n5-uuid", b1UUID, 7, "n5 body", 1541108743, true, true) + database.MustExec(t, "inserting n9", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n9-uuid", b1UUID, 17, "n9 body", 1541108743, true, false) + database.MustExec(t, "inserting n10", db, "INSERT INTO notes (uuid, book_uuid, usn, body, added_on, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", "n10-uuid", b1UUID, 0, "n10 body", 1541108743, false, false) // execute tx, err := db.Begin() @@ -3168,21 +3112,19 @@ func TestCleanLocalNotes(t *testing.T) { // test var noteCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.AssertEqual(t, noteCount, 3, "note count mismatch") + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + assert.Equal(t, noteCount, 3, "note count mismatch") - var n1, n2, n6 core.Note - testutils.MustScan(t, "getting n1", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", "n1-uuid"), &n1.Dirty) - testutils.MustScan(t, "getting n2", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", "n2-uuid"), &n2.Dirty) - testutils.MustScan(t, "getting n6", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", "n6-uuid"), &n6.Dirty) + var n1, n2, n6 database.Note + database.MustScan(t, "getting n1", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", "n1-uuid"), &n1.Dirty) + database.MustScan(t, "getting n2", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", "n2-uuid"), &n2.Dirty) + database.MustScan(t, "getting n6", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", "n6-uuid"), &n6.Dirty) } func TestCleanLocalBooks(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../../tmp", "../../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB + db := database.InitTestDB(t, "../../tmp/.dnote", nil) + defer database.CloseTestDB(t, db) list := syncList{ Notes: map[string]client.SyncFragNote{ @@ -3214,14 +3156,14 @@ func TestCleanLocalBooks(t *testing.T) { } // existent in the server - testutils.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 1, false, false) - testutils.MustExec(t, "inserting b3", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b3-uuid", "b3-label", 0, false, true) + database.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b1-uuid", "b1-label", 1, false, false) + database.MustExec(t, "inserting b3", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b3-uuid", "b3-label", 0, false, true) // non-existent in the server but in valid state - testutils.MustExec(t, "inserting b5", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b5-uuid", "b5-label", 0, true, true) + database.MustExec(t, "inserting b5", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b5-uuid", "b5-label", 0, true, true) // non-existent in the server and in an invalid state - testutils.MustExec(t, "inserting b6", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b6-uuid", "b6-label", 10, true, true) - testutils.MustExec(t, "inserting b7", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b7-uuid", "b7-label", 11, false, false) - testutils.MustExec(t, "inserting b8", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b8-uuid", "b8-label", 0, false, false) + database.MustExec(t, "inserting b6", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b6-uuid", "b6-label", 10, true, true) + database.MustExec(t, "inserting b7", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b7-uuid", "b7-label", 11, false, false) + database.MustExec(t, "inserting b8", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", "b8-uuid", "b8-label", 0, false, false) // execute tx, err := db.Begin() @@ -3238,11 +3180,11 @@ func TestCleanLocalBooks(t *testing.T) { // test var bookCount int - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqual(t, bookCount, 3, "note count mismatch") + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + assert.Equal(t, bookCount, 3, "note count mismatch") - var b1, b3, b5 core.Book - testutils.MustScan(t, "getting b1", db.QueryRow("SELECT label FROM books WHERE uuid = ?", "b1-uuid"), &b1.Label) - testutils.MustScan(t, "getting b3", db.QueryRow("SELECT label FROM books WHERE uuid = ?", "b3-uuid"), &b3.Label) - testutils.MustScan(t, "getting b5", db.QueryRow("SELECT label FROM books WHERE uuid = ?", "b5-uuid"), &b5.Label) + var b1, b3, b5 database.Book + database.MustScan(t, "getting b1", db.QueryRow("SELECT label FROM books WHERE uuid = ?", "b1-uuid"), &b1.Label) + database.MustScan(t, "getting b3", db.QueryRow("SELECT label FROM books WHERE uuid = ?", "b3-uuid"), &b3.Label) + database.MustScan(t, "getting b5", db.QueryRow("SELECT label FROM books WHERE uuid = ?", "b5-uuid"), &b5.Label) } diff --git a/cli/cmd/version/version.go b/pkg/cli/cmd/version/version.go similarity index 92% rename from cli/cmd/version/version.go rename to pkg/cli/cmd/version/version.go index 27b530e8..456b0602 100644 --- a/cli/cmd/version/version.go +++ b/pkg/cli/cmd/version/version.go @@ -21,12 +21,12 @@ package version import ( "fmt" - "github.com/dnote/dnote/cli/infra" + "github.com/dnote/dnote/pkg/cli/context" "github.com/spf13/cobra" ) // NewCmd returns a new version command -func NewCmd(ctx infra.DnoteCtx) *cobra.Command { +func NewCmd(ctx context.DnoteCtx) *cobra.Command { cmd := &cobra.Command{ Use: "version", Short: "Print the version number of Dnote", diff --git a/cli/cmd/view/view.go b/pkg/cli/cmd/view/view.go similarity index 86% rename from cli/cmd/view/view.go rename to pkg/cli/cmd/view/view.go index 6998cb8f..4e255639 100644 --- a/cli/cmd/view/view.go +++ b/pkg/cli/cmd/view/view.go @@ -19,14 +19,14 @@ package view import ( - "github.com/dnote/dnote/cli/core" - "github.com/dnote/dnote/cli/infra" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/infra" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/dnote/dnote/cli/cmd/cat" - "github.com/dnote/dnote/cli/cmd/ls" - "github.com/dnote/dnote/cli/utils" + "github.com/dnote/dnote/pkg/cli/cmd/cat" + "github.com/dnote/dnote/pkg/cli/cmd/ls" + "github.com/dnote/dnote/pkg/cli/utils" ) var example = ` @@ -51,7 +51,7 @@ func preRun(cmd *cobra.Command, args []string) error { } // NewCmd returns a new view command -func NewCmd(ctx infra.DnoteCtx) *cobra.Command { +func NewCmd(ctx context.DnoteCtx) *cobra.Command { cmd := &cobra.Command{ Use: "view ", Aliases: []string{"v"}, @@ -67,9 +67,9 @@ func NewCmd(ctx infra.DnoteCtx) *cobra.Command { return cmd } -func newRun(ctx infra.DnoteCtx) core.RunEFunc { +func newRun(ctx context.DnoteCtx) infra.RunEFunc { return func(cmd *cobra.Command, args []string) error { - var run core.RunEFunc + var run infra.RunEFunc if len(args) == 0 { run = ls.NewRun(ctx, nameOnly) diff --git a/pkg/cli/consts/consts.go b/pkg/cli/consts/consts.go new file mode 100644 index 00000000..87137941 --- /dev/null +++ b/pkg/cli/consts/consts.go @@ -0,0 +1,46 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +// Package consts provides definitions of constants +package consts + +var ( + // DnoteDirName is the name of the directory containing dnote files + DnoteDirName = ".dnote" + // DnoteDBFileName is a filename for the Dnote SQLite database + DnoteDBFileName = "dnote.db" + // TmpContentFilename is the filename for a temporary content + TmpContentFilename = "DNOTE_TMPCONTENT.md" + + // SystemSchema is the key for schema in the system table + SystemSchema = "schema" + // SystemRemoteSchema is the key for remote schema in the system table + SystemRemoteSchema = "remote_schema" + // SystemLastSyncAt is the timestamp of the server at the last sync + SystemLastSyncAt = "last_sync_time" + // SystemLastMaxUSN is the user's max_usn from the server at the alst sync + SystemLastMaxUSN = "last_max_usn" + // SystemLastUpgrade is the timestamp at which the system more recently checked for an upgrade + SystemLastUpgrade = "last_upgrade" + // SystemCipherKey is the encryption key + SystemCipherKey = "enc_key" + // SystemSessionKey is the session key + SystemSessionKey = "session_token" + // SystemSessionKeyExpiry is the timestamp at which the session key will expire + SystemSessionKeyExpiry = "session_token_expiry" +) diff --git a/pkg/cli/context/ctx.go b/pkg/cli/context/ctx.go new file mode 100644 index 00000000..44112c4c --- /dev/null +++ b/pkg/cli/context/ctx.go @@ -0,0 +1,36 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +// Package context defines dnote context +package context + +import ( + "github.com/dnote/dnote/pkg/cli/database" +) + +// DnoteCtx is a context holding the information of the current runtime +type DnoteCtx struct { + HomeDir string + DnoteDir string + APIEndpoint string + Version string + DB *database.DB + SessionKey string + SessionKeyExpiry int64 + CipherKey []byte +} diff --git a/pkg/cli/context/operations.go b/pkg/cli/context/operations.go new file mode 100644 index 00000000..fe1aeb1f --- /dev/null +++ b/pkg/cli/context/operations.go @@ -0,0 +1,48 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +package context + +import ( + "encoding/base64" + + "github.com/dnote/dnote/pkg/cli/consts" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/pkg/errors" +) + +// GetCipherKey retrieves the cipher key and decode the base64 into bytes. +func (ctx DnoteCtx) GetCipherKey() ([]byte, error) { + tx, err := ctx.DB.Begin() + if err != nil { + return nil, errors.Wrap(err, "beginning transaction") + } + + var cipherKeyB64 string + err = database.GetSystem(tx, consts.SystemCipherKey, &cipherKeyB64) + if err != nil { + return []byte{}, errors.Wrap(err, "getting enc key") + } + + cipherKey, err := base64.StdEncoding.DecodeString(cipherKeyB64) + if err != nil { + return nil, errors.Wrap(err, "decoding cipherKey from base64") + } + + return cipherKey, nil +} diff --git a/pkg/cli/context/testutils.go b/pkg/cli/context/testutils.go new file mode 100644 index 00000000..2e9043fa --- /dev/null +++ b/pkg/cli/context/testutils.go @@ -0,0 +1,41 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +package context + +import ( + "fmt" + "testing" + + "github.com/dnote/dnote/pkg/cli/consts" + "github.com/dnote/dnote/pkg/cli/database" +) + +// InitTestCtx initializes a test context +func InitTestCtx(t *testing.T, dnoteDir string, dbOpts *database.TestDBOptions) DnoteCtx { + dbPath := fmt.Sprintf("%s/%s", dnoteDir, consts.DnoteDBFileName) + + db := database.InitTestDB(t, dbPath, dbOpts) + + return DnoteCtx{DB: db, DnoteDir: dnoteDir} +} + +// TeardownTestCtx cleans up the test context +func TeardownTestCtx(t *testing.T, ctx DnoteCtx) { + database.CloseTestDB(t, ctx.DB) +} diff --git a/cli/crypt/utils.go b/pkg/cli/crypt/crypto.go similarity index 97% rename from cli/crypt/utils.go rename to pkg/cli/crypt/crypto.go index 61f00800..f949b776 100644 --- a/cli/crypt/utils.go +++ b/pkg/cli/crypt/crypto.go @@ -27,7 +27,6 @@ import ( "encoding/base64" "io" - "github.com/dnote/dnote/cli/log" "github.com/pkg/errors" "golang.org/x/crypto/hkdf" "golang.org/x/crypto/pbkdf2" @@ -51,7 +50,6 @@ func runHkdf(secret, salt, info []byte) ([]byte, error) { // and an authentication key func MakeKeys(password, email []byte, iteration int) ([]byte, []byte, error) { masterKey := pbkdf2.Key([]byte(password), []byte(email), iteration, 32, sha256.New) - log.Debug("email: %s, password: %s", email, password) authKey, err := runHkdf(masterKey, email, []byte("auth")) if err != nil { diff --git a/cli/crypt/utils_test.go b/pkg/cli/crypt/crypto_test.go similarity index 93% rename from cli/crypt/utils_test.go rename to pkg/cli/crypt/crypto_test.go index 729ddb5c..ee63df2e 100644 --- a/cli/crypt/utils_test.go +++ b/pkg/cli/crypt/crypto_test.go @@ -25,7 +25,7 @@ import ( "fmt" "testing" - "github.com/dnote/dnote/cli/testutils" + "github.com/dnote/dnote/pkg/assert" "github.com/pkg/errors" ) @@ -77,7 +77,7 @@ func TestAesGcmEncrypt(t *testing.T) { t.Fatal(errors.Wrap(err, "decode")) } - testutils.AssertDeepEqual(t, plaintext, tc.plaintext, "plaintext mismatch") + assert.DeepEqual(t, plaintext, tc.plaintext, "plaintext mismatch") }) } } @@ -112,7 +112,7 @@ func TestAesGcmDecrypt(t *testing.T) { t.Fatal(errors.Wrap(err, "performing decryption")) } - testutils.AssertDeepEqual(t, plaintext, []byte(tc.expectedPlaintext), "plaintext mismatch") + assert.DeepEqual(t, plaintext, []byte(tc.expectedPlaintext), "plaintext mismatch") }) } } diff --git a/cli/core/models.go b/pkg/cli/database/models.go similarity index 90% rename from cli/core/models.go rename to pkg/cli/database/models.go index b41e05d8..0fe26c37 100644 --- a/cli/core/models.go +++ b/pkg/cli/database/models.go @@ -16,10 +16,9 @@ * along with Dnote CLI. If not, see . */ -package core +package database import ( - "github.com/dnote/dnote/cli/infra" "github.com/pkg/errors" ) @@ -62,7 +61,7 @@ func NewNote(uuid, bookUUID, body string, addedOn, editedOn int64, usn int, publ } // Insert inserts a new note -func (n Note) Insert(db *infra.DB) error { +func (n Note) Insert(db *DB) error { _, err := db.Exec("INSERT INTO notes (uuid, book_uuid, body, added_on, edited_on, usn, public, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", n.UUID, n.BookUUID, n.Body, n.AddedOn, n.EditedOn, n.USN, n.Public, n.Deleted, n.Dirty) @@ -74,7 +73,7 @@ func (n Note) Insert(db *infra.DB) error { } // Update updates the note with the given data -func (n Note) Update(db *infra.DB) error { +func (n Note) Update(db *DB) error { _, err := db.Exec("UPDATE notes SET book_uuid = ?, body = ?, added_on = ?, edited_on = ?, usn = ?, public = ?, deleted = ?, dirty = ? WHERE uuid = ?", n.BookUUID, n.Body, n.AddedOn, n.EditedOn, n.USN, n.Public, n.Deleted, n.Dirty, n.UUID) @@ -86,7 +85,7 @@ func (n Note) Update(db *infra.DB) error { } // UpdateUUID updates the uuid of a book -func (n *Note) UpdateUUID(db *infra.DB, newUUID string) error { +func (n *Note) UpdateUUID(db *DB, newUUID string) error { _, err := db.Exec("UPDATE notes SET uuid = ? WHERE uuid = ?", newUUID, n.UUID) if err != nil { @@ -99,7 +98,7 @@ func (n *Note) UpdateUUID(db *infra.DB, newUUID string) error { } // Expunge hard-deletes the note from the database -func (n Note) Expunge(db *infra.DB) error { +func (n Note) Expunge(db *DB) error { _, err := db.Exec("DELETE FROM notes WHERE uuid = ?", n.UUID) if err != nil { return errors.Wrap(err, "expunging a note locally") @@ -120,7 +119,7 @@ func NewBook(uuid, label string, usn int, deleted, dirty bool) Book { } // Insert inserts a new book -func (b Book) Insert(db *infra.DB) error { +func (b Book) Insert(db *DB) error { _, err := db.Exec("INSERT INTO books (uuid, label, usn, dirty, deleted) VALUES (?, ?, ?, ?, ?)", b.UUID, b.Label, b.USN, b.Dirty, b.Deleted) @@ -132,7 +131,7 @@ func (b Book) Insert(db *infra.DB) error { } // Update updates the book with the given data -func (b Book) Update(db *infra.DB) error { +func (b Book) Update(db *DB) error { _, err := db.Exec("UPDATE books SET label = ?, usn = ?, dirty = ?, deleted = ? WHERE uuid = ?", b.Label, b.USN, b.Dirty, b.Deleted, b.UUID) @@ -144,7 +143,7 @@ func (b Book) Update(db *infra.DB) error { } // UpdateUUID updates the uuid of a book -func (b *Book) UpdateUUID(db *infra.DB, newUUID string) error { +func (b *Book) UpdateUUID(db *DB, newUUID string) error { _, err := db.Exec("UPDATE books SET uuid = ? WHERE uuid = ?", newUUID, b.UUID) if err != nil { @@ -157,7 +156,7 @@ func (b *Book) UpdateUUID(db *infra.DB, newUUID string) error { } // Expunge hard-deletes the book from the database -func (b Book) Expunge(db *infra.DB) error { +func (b Book) Expunge(db *DB) error { _, err := db.Exec("DELETE FROM books WHERE uuid = ?", b.UUID) if err != nil { return errors.Wrap(err, "expunging a book locally") diff --git a/cli/core/models_test.go b/pkg/cli/database/models_test.go similarity index 55% rename from cli/core/models_test.go rename to pkg/cli/database/models_test.go index bb3c8369..bdf10e18 100644 --- a/cli/core/models_test.go +++ b/pkg/cli/database/models_test.go @@ -16,13 +16,13 @@ * along with Dnote CLI. If not, see . */ -package core +package database import ( "fmt" "testing" - "github.com/dnote/dnote/cli/testutils" + "github.com/dnote/dnote/pkg/assert" "github.com/pkg/errors" ) @@ -65,15 +65,15 @@ func TestNewNote(t *testing.T) { for idx, tc := range testCases { got := NewNote(tc.uuid, tc.bookUUID, tc.body, tc.addedOn, tc.editedOn, tc.usn, tc.public, tc.deleted, tc.dirty) - testutils.AssertEqual(t, got.UUID, tc.uuid, fmt.Sprintf("UUID mismatch for test case %d", idx)) - testutils.AssertEqual(t, got.BookUUID, tc.bookUUID, fmt.Sprintf("BookUUID mismatch for test case %d", idx)) - testutils.AssertEqual(t, got.Body, tc.body, fmt.Sprintf("Body mismatch for test case %d", idx)) - testutils.AssertEqual(t, got.AddedOn, tc.addedOn, fmt.Sprintf("AddedOn mismatch for test case %d", idx)) - testutils.AssertEqual(t, got.EditedOn, tc.editedOn, fmt.Sprintf("EditedOn mismatch for test case %d", idx)) - testutils.AssertEqual(t, got.USN, tc.usn, fmt.Sprintf("USN mismatch for test case %d", idx)) - testutils.AssertEqual(t, got.Public, tc.public, fmt.Sprintf("Public mismatch for test case %d", idx)) - testutils.AssertEqual(t, got.Deleted, tc.deleted, fmt.Sprintf("Deleted mismatch for test case %d", idx)) - testutils.AssertEqual(t, got.Dirty, tc.dirty, fmt.Sprintf("Dirty mismatch for test case %d", idx)) + assert.Equal(t, got.UUID, tc.uuid, fmt.Sprintf("UUID mismatch for test case %d", idx)) + assert.Equal(t, got.BookUUID, tc.bookUUID, fmt.Sprintf("BookUUID mismatch for test case %d", idx)) + assert.Equal(t, got.Body, tc.body, fmt.Sprintf("Body mismatch for test case %d", idx)) + assert.Equal(t, got.AddedOn, tc.addedOn, fmt.Sprintf("AddedOn mismatch for test case %d", idx)) + assert.Equal(t, got.EditedOn, tc.editedOn, fmt.Sprintf("EditedOn mismatch for test case %d", idx)) + assert.Equal(t, got.USN, tc.usn, fmt.Sprintf("USN mismatch for test case %d", idx)) + assert.Equal(t, got.Public, tc.public, fmt.Sprintf("Public mismatch for test case %d", idx)) + assert.Equal(t, got.Deleted, tc.deleted, fmt.Sprintf("Deleted mismatch for test case %d", idx)) + assert.Equal(t, got.Dirty, tc.dirty, fmt.Sprintf("Dirty mismatch for test case %d", idx)) } } @@ -116,8 +116,8 @@ func TestNoteInsert(t *testing.T) { for idx, tc := range testCases { func() { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := InitTestDB(t, "../tmp/dnote-test.db", nil) + defer CloseTestDB(t, db) n := Note{ UUID: tc.uuid, @@ -132,8 +132,6 @@ func TestNoteInsert(t *testing.T) { } // execute - db := ctx.DB - tx, err := db.Begin() if err != nil { t.Fatalf(errors.Wrap(err, fmt.Sprintf("beginning a transaction for test case %d", idx)).Error()) @@ -151,19 +149,19 @@ func TestNoteInsert(t *testing.T) { var addedOn, editedOn int64 var usn int var public, deleted, dirty bool - testutils.MustScan(t, "getting n1", + MustScan(t, "getting n1", db.QueryRow("SELECT uuid, book_uuid, body, added_on, edited_on, usn, public, deleted, dirty FROM notes WHERE uuid = ?", tc.uuid), &uuid, &bookUUID, &body, &addedOn, &editedOn, &usn, &public, &deleted, &dirty) - testutils.AssertEqual(t, uuid, tc.uuid, fmt.Sprintf("uuid mismatch for test case %d", idx)) - testutils.AssertEqual(t, bookUUID, tc.bookUUID, fmt.Sprintf("bookUUID mismatch for test case %d", idx)) - testutils.AssertEqual(t, body, tc.body, fmt.Sprintf("body mismatch for test case %d", idx)) - testutils.AssertEqual(t, addedOn, tc.addedOn, fmt.Sprintf("addedOn mismatch for test case %d", idx)) - testutils.AssertEqual(t, editedOn, tc.editedOn, fmt.Sprintf("editedOn mismatch for test case %d", idx)) - testutils.AssertEqual(t, usn, tc.usn, fmt.Sprintf("usn mismatch for test case %d", idx)) - testutils.AssertEqual(t, public, tc.public, fmt.Sprintf("public mismatch for test case %d", idx)) - testutils.AssertEqual(t, deleted, tc.deleted, fmt.Sprintf("deleted mismatch for test case %d", idx)) - testutils.AssertEqual(t, dirty, tc.dirty, fmt.Sprintf("dirty mismatch for test case %d", idx)) + assert.Equal(t, uuid, tc.uuid, fmt.Sprintf("uuid mismatch for test case %d", idx)) + assert.Equal(t, bookUUID, tc.bookUUID, fmt.Sprintf("bookUUID mismatch for test case %d", idx)) + assert.Equal(t, body, tc.body, fmt.Sprintf("body mismatch for test case %d", idx)) + assert.Equal(t, addedOn, tc.addedOn, fmt.Sprintf("addedOn mismatch for test case %d", idx)) + assert.Equal(t, editedOn, tc.editedOn, fmt.Sprintf("editedOn mismatch for test case %d", idx)) + assert.Equal(t, usn, tc.usn, fmt.Sprintf("usn mismatch for test case %d", idx)) + assert.Equal(t, public, tc.public, fmt.Sprintf("public mismatch for test case %d", idx)) + assert.Equal(t, deleted, tc.deleted, fmt.Sprintf("deleted mismatch for test case %d", idx)) + assert.Equal(t, dirty, tc.dirty, fmt.Sprintf("dirty mismatch for test case %d", idx)) }() } } @@ -264,8 +262,8 @@ func TestNoteUpdate(t *testing.T) { for idx, tc := range testCases { func() { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := InitTestDB(t, "../tmp/dnote-test.db", nil) + defer CloseTestDB(t, db) n1 := Note{ UUID: tc.uuid, @@ -290,9 +288,8 @@ func TestNoteUpdate(t *testing.T) { Dirty: false, } - db := ctx.DB - testutils.MustExec(t, fmt.Sprintf("inserting n1 for test case %d", idx), db, "INSERT INTO notes (uuid, book_uuid, usn, added_on, edited_on, body, public, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", n1.UUID, n1.BookUUID, n1.USN, n1.AddedOn, n1.EditedOn, n1.Body, n1.Public, n1.Deleted, n1.Dirty) - testutils.MustExec(t, fmt.Sprintf("inserting n2 for test case %d", idx), db, "INSERT INTO notes (uuid, book_uuid, usn, added_on, edited_on, body, public, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", n2.UUID, n2.BookUUID, n2.USN, n2.AddedOn, n2.EditedOn, n2.Body, n2.Public, n2.Deleted, n2.Dirty) + MustExec(t, fmt.Sprintf("inserting n1 for test case %d", idx), db, "INSERT INTO notes (uuid, book_uuid, usn, added_on, edited_on, body, public, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", n1.UUID, n1.BookUUID, n1.USN, n1.AddedOn, n1.EditedOn, n1.Body, n1.Public, n1.Deleted, n1.Dirty) + MustExec(t, fmt.Sprintf("inserting n2 for test case %d", idx), db, "INSERT INTO notes (uuid, book_uuid, usn, added_on, edited_on, body, public, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", n2.UUID, n2.BookUUID, n2.USN, n2.AddedOn, n2.EditedOn, n2.Body, n2.Public, n2.Deleted, n2.Dirty) // execute tx, err := db.Begin() @@ -317,32 +314,32 @@ func TestNoteUpdate(t *testing.T) { // test var n1Record, n2Record Note - testutils.MustScan(t, "getting n1", + MustScan(t, "getting n1", db.QueryRow("SELECT uuid, book_uuid, body, added_on, edited_on, usn, public, deleted, dirty FROM notes WHERE uuid = ?", tc.uuid), &n1Record.UUID, &n1Record.BookUUID, &n1Record.Body, &n1Record.AddedOn, &n1Record.EditedOn, &n1Record.USN, &n1Record.Public, &n1Record.Deleted, &n1Record.Dirty) - testutils.MustScan(t, "getting n2", + MustScan(t, "getting n2", db.QueryRow("SELECT uuid, book_uuid, body, added_on, edited_on, usn, public, deleted, dirty FROM notes WHERE uuid = ?", n2.UUID), &n2Record.UUID, &n2Record.BookUUID, &n2Record.Body, &n2Record.AddedOn, &n2Record.EditedOn, &n2Record.USN, &n2Record.Public, &n2Record.Deleted, &n2Record.Dirty) - testutils.AssertEqual(t, n1Record.UUID, n1.UUID, fmt.Sprintf("n1 uuid mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.BookUUID, tc.newBookUUID, fmt.Sprintf("n1 bookUUID mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.Body, tc.newBody, fmt.Sprintf("n1 body mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.AddedOn, n1.AddedOn, fmt.Sprintf("n1 addedOn mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.EditedOn, tc.newEditedOn, fmt.Sprintf("n1 editedOn mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.USN, tc.newUSN, fmt.Sprintf("n1 usn mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.Public, tc.newPublic, fmt.Sprintf("n1 public mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.Deleted, tc.newDeleted, fmt.Sprintf("n1 deleted mismatch for test case %d", idx)) - testutils.AssertEqual(t, n1Record.Dirty, tc.newDirty, fmt.Sprintf("n1 dirty mismatch for test case %d", idx)) + assert.Equal(t, n1Record.UUID, n1.UUID, fmt.Sprintf("n1 uuid mismatch for test case %d", idx)) + assert.Equal(t, n1Record.BookUUID, tc.newBookUUID, fmt.Sprintf("n1 bookUUID mismatch for test case %d", idx)) + assert.Equal(t, n1Record.Body, tc.newBody, fmt.Sprintf("n1 body mismatch for test case %d", idx)) + assert.Equal(t, n1Record.AddedOn, n1.AddedOn, fmt.Sprintf("n1 addedOn mismatch for test case %d", idx)) + assert.Equal(t, n1Record.EditedOn, tc.newEditedOn, fmt.Sprintf("n1 editedOn mismatch for test case %d", idx)) + assert.Equal(t, n1Record.USN, tc.newUSN, fmt.Sprintf("n1 usn mismatch for test case %d", idx)) + assert.Equal(t, n1Record.Public, tc.newPublic, fmt.Sprintf("n1 public mismatch for test case %d", idx)) + assert.Equal(t, n1Record.Deleted, tc.newDeleted, fmt.Sprintf("n1 deleted mismatch for test case %d", idx)) + assert.Equal(t, n1Record.Dirty, tc.newDirty, fmt.Sprintf("n1 dirty mismatch for test case %d", idx)) - testutils.AssertEqual(t, n2Record.UUID, n2.UUID, fmt.Sprintf("n2 uuid mismatch for test case %d", idx)) - testutils.AssertEqual(t, n2Record.BookUUID, n2.BookUUID, fmt.Sprintf("n2 bookUUID mismatch for test case %d", idx)) - testutils.AssertEqual(t, n2Record.Body, n2.Body, fmt.Sprintf("n2 body mismatch for test case %d", idx)) - testutils.AssertEqual(t, n2Record.AddedOn, n2.AddedOn, fmt.Sprintf("n2 addedOn mismatch for test case %d", idx)) - testutils.AssertEqual(t, n2Record.EditedOn, n2.EditedOn, fmt.Sprintf("n2 editedOn mismatch for test case %d", idx)) - testutils.AssertEqual(t, n2Record.USN, n2.USN, fmt.Sprintf("n2 usn mismatch for test case %d", idx)) - testutils.AssertEqual(t, n2Record.Public, n2.Public, fmt.Sprintf("n2 public mismatch for test case %d", idx)) - testutils.AssertEqual(t, n2Record.Deleted, n2.Deleted, fmt.Sprintf("n2 deleted mismatch for test case %d", idx)) - testutils.AssertEqual(t, n2Record.Dirty, n2.Dirty, fmt.Sprintf("n2 dirty mismatch for test case %d", idx)) + assert.Equal(t, n2Record.UUID, n2.UUID, fmt.Sprintf("n2 uuid mismatch for test case %d", idx)) + assert.Equal(t, n2Record.BookUUID, n2.BookUUID, fmt.Sprintf("n2 bookUUID mismatch for test case %d", idx)) + assert.Equal(t, n2Record.Body, n2.Body, fmt.Sprintf("n2 body mismatch for test case %d", idx)) + assert.Equal(t, n2Record.AddedOn, n2.AddedOn, fmt.Sprintf("n2 addedOn mismatch for test case %d", idx)) + assert.Equal(t, n2Record.EditedOn, n2.EditedOn, fmt.Sprintf("n2 editedOn mismatch for test case %d", idx)) + assert.Equal(t, n2Record.USN, n2.USN, fmt.Sprintf("n2 usn mismatch for test case %d", idx)) + assert.Equal(t, n2Record.Public, n2.Public, fmt.Sprintf("n2 public mismatch for test case %d", idx)) + assert.Equal(t, n2Record.Deleted, n2.Deleted, fmt.Sprintf("n2 deleted mismatch for test case %d", idx)) + assert.Equal(t, n2Record.Dirty, n2.Dirty, fmt.Sprintf("n2 dirty mismatch for test case %d", idx)) }() } } @@ -362,8 +359,8 @@ func TestNoteUpdateUUID(t *testing.T) { for idx, tc := range testCases { t.Run(fmt.Sprintf("testCase%d", idx), func(t *testing.T) { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := InitTestDB(t, "../tmp/dnote-test.db", nil) + defer CloseTestDB(t, db) n1 := Note{ UUID: "n1-uuid", @@ -384,9 +381,8 @@ func TestNoteUpdateUUID(t *testing.T) { Dirty: false, } - db := ctx.DB - testutils.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, body, added_on, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", n1.UUID, n1.BookUUID, n1.Body, n1.AddedOn, n1.USN, n1.Deleted, n1.Dirty) - testutils.MustExec(t, "inserting n2", db, "INSERT INTO notes (uuid, book_uuid, body, added_on, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", n2.UUID, n2.BookUUID, n2.Body, n2.AddedOn, n2.USN, n2.Deleted, n2.Dirty) + MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, body, added_on, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", n1.UUID, n1.BookUUID, n1.Body, n1.AddedOn, n1.USN, n1.Deleted, n1.Dirty) + MustExec(t, "inserting n2", db, "INSERT INTO notes (uuid, book_uuid, body, added_on, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?)", n2.UUID, n2.BookUUID, n2.Body, n2.AddedOn, n2.USN, n2.Deleted, n2.Dirty) // execute tx, err := db.Begin() @@ -402,24 +398,24 @@ func TestNoteUpdateUUID(t *testing.T) { // test var n1Record, n2Record Note - testutils.MustScan(t, "getting n1", + MustScan(t, "getting n1", db.QueryRow("SELECT uuid, body, usn, deleted, dirty FROM notes WHERE body = ?", "n1-body"), &n1Record.UUID, &n1Record.Body, &n1Record.USN, &n1Record.Deleted, &n1Record.Dirty) - testutils.MustScan(t, "getting n2", + MustScan(t, "getting n2", db.QueryRow("SELECT uuid, body, usn, deleted, dirty FROM notes WHERE body = ?", "n2-body"), &n2Record.UUID, &n2Record.Body, &n2Record.USN, &n2Record.Deleted, &n2Record.Dirty) - testutils.AssertEqual(t, n1.UUID, tc.newUUID, "n1 original reference uuid mismatch") - testutils.AssertEqual(t, n1Record.UUID, tc.newUUID, "n1 uuid mismatch") - testutils.AssertEqual(t, n2Record.UUID, n2.UUID, "n2 uuid mismatch") + assert.Equal(t, n1.UUID, tc.newUUID, "n1 original reference uuid mismatch") + assert.Equal(t, n1Record.UUID, tc.newUUID, "n1 uuid mismatch") + assert.Equal(t, n2Record.UUID, n2.UUID, "n2 uuid mismatch") }) } } func TestNoteExpunge(t *testing.T) { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := InitTestDB(t, "../tmp/dnote-test.db", nil) + defer CloseTestDB(t, db) n1 := Note{ UUID: "n1-uuid", @@ -444,9 +440,8 @@ func TestNoteExpunge(t *testing.T) { Dirty: false, } - db := ctx.DB - testutils.MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, added_on, edited_on, body, public, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", n1.UUID, n1.BookUUID, n1.USN, n1.AddedOn, n1.EditedOn, n1.Body, n1.Public, n1.Deleted, n1.Dirty) - testutils.MustExec(t, "inserting n2", db, "INSERT INTO notes (uuid, book_uuid, usn, added_on, edited_on, body, public, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", n2.UUID, n2.BookUUID, n2.USN, n2.AddedOn, n2.EditedOn, n2.Body, n2.Public, n2.Deleted, n2.Dirty) + MustExec(t, "inserting n1", db, "INSERT INTO notes (uuid, book_uuid, usn, added_on, edited_on, body, public, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", n1.UUID, n1.BookUUID, n1.USN, n1.AddedOn, n1.EditedOn, n1.Body, n1.Public, n1.Deleted, n1.Dirty) + MustExec(t, "inserting n2", db, "INSERT INTO notes (uuid, book_uuid, usn, added_on, edited_on, body, public, deleted, dirty) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", n2.UUID, n2.BookUUID, n2.USN, n2.AddedOn, n2.EditedOn, n2.Body, n2.Public, n2.Deleted, n2.Dirty) // execute tx, err := db.Begin() @@ -463,24 +458,24 @@ func TestNoteExpunge(t *testing.T) { // test var noteCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.AssertEqualf(t, noteCount, 1, "note count mismatch") + assert.Equalf(t, noteCount, 1, "note count mismatch") var n2Record Note - testutils.MustScan(t, "getting n2", + MustScan(t, "getting n2", db.QueryRow("SELECT uuid, book_uuid, body, added_on, edited_on, usn, public, deleted, dirty FROM notes WHERE uuid = ?", n2.UUID), &n2Record.UUID, &n2Record.BookUUID, &n2Record.Body, &n2Record.AddedOn, &n2Record.EditedOn, &n2Record.USN, &n2Record.Public, &n2Record.Deleted, &n2Record.Dirty) - testutils.AssertEqual(t, n2Record.UUID, n2.UUID, "n2 uuid mismatch") - testutils.AssertEqual(t, n2Record.BookUUID, n2.BookUUID, "n2 bookUUID mismatch") - testutils.AssertEqual(t, n2Record.Body, n2.Body, "n2 body mismatch") - testutils.AssertEqual(t, n2Record.AddedOn, n2.AddedOn, "n2 addedOn mismatch") - testutils.AssertEqual(t, n2Record.EditedOn, n2.EditedOn, "n2 editedOn mismatch") - testutils.AssertEqual(t, n2Record.USN, n2.USN, "n2 usn mismatch") - testutils.AssertEqual(t, n2Record.Public, n2.Public, "n2 public mismatch") - testutils.AssertEqual(t, n2Record.Deleted, n2.Deleted, "n2 deleted mismatch") - testutils.AssertEqual(t, n2Record.Dirty, n2.Dirty, "n2 dirty mismatch") + assert.Equal(t, n2Record.UUID, n2.UUID, "n2 uuid mismatch") + assert.Equal(t, n2Record.BookUUID, n2.BookUUID, "n2 bookUUID mismatch") + assert.Equal(t, n2Record.Body, n2.Body, "n2 body mismatch") + assert.Equal(t, n2Record.AddedOn, n2.AddedOn, "n2 addedOn mismatch") + assert.Equal(t, n2Record.EditedOn, n2.EditedOn, "n2 editedOn mismatch") + assert.Equal(t, n2Record.USN, n2.USN, "n2 usn mismatch") + assert.Equal(t, n2Record.Public, n2.Public, "n2 public mismatch") + assert.Equal(t, n2Record.Deleted, n2.Deleted, "n2 deleted mismatch") + assert.Equal(t, n2Record.Dirty, n2.Dirty, "n2 dirty mismatch") } func TestNewBook(t *testing.T) { @@ -510,11 +505,11 @@ func TestNewBook(t *testing.T) { for idx, tc := range testCases { got := NewBook(tc.uuid, tc.label, tc.usn, tc.deleted, tc.dirty) - testutils.AssertEqual(t, got.UUID, tc.uuid, fmt.Sprintf("UUID mismatch for test case %d", idx)) - testutils.AssertEqual(t, got.Label, tc.label, fmt.Sprintf("Label mismatch for test case %d", idx)) - testutils.AssertEqual(t, got.USN, tc.usn, fmt.Sprintf("USN mismatch for test case %d", idx)) - testutils.AssertEqual(t, got.Deleted, tc.deleted, fmt.Sprintf("Deleted mismatch for test case %d", idx)) - testutils.AssertEqual(t, got.Dirty, tc.dirty, fmt.Sprintf("Dirty mismatch for test case %d", idx)) + assert.Equal(t, got.UUID, tc.uuid, fmt.Sprintf("UUID mismatch for test case %d", idx)) + assert.Equal(t, got.Label, tc.label, fmt.Sprintf("Label mismatch for test case %d", idx)) + assert.Equal(t, got.USN, tc.usn, fmt.Sprintf("USN mismatch for test case %d", idx)) + assert.Equal(t, got.Deleted, tc.deleted, fmt.Sprintf("Deleted mismatch for test case %d", idx)) + assert.Equal(t, got.Dirty, tc.dirty, fmt.Sprintf("Dirty mismatch for test case %d", idx)) } } @@ -545,8 +540,8 @@ func TestBookInsert(t *testing.T) { for idx, tc := range testCases { func() { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := InitTestDB(t, "../tmp/dnote-test.db", nil) + defer CloseTestDB(t, db) b := Book{ UUID: tc.uuid, @@ -557,7 +552,6 @@ func TestBookInsert(t *testing.T) { } // execute - db := ctx.DB tx, err := db.Begin() if err != nil { @@ -575,15 +569,15 @@ func TestBookInsert(t *testing.T) { var uuid, label string var usn int var deleted, dirty bool - testutils.MustScan(t, "getting b1", + MustScan(t, "getting b1", db.QueryRow("SELECT uuid, label, usn, deleted, dirty FROM books WHERE uuid = ?", tc.uuid), &uuid, &label, &usn, &deleted, &dirty) - testutils.AssertEqual(t, uuid, tc.uuid, fmt.Sprintf("uuid mismatch for test case %d", idx)) - testutils.AssertEqual(t, label, tc.label, fmt.Sprintf("label mismatch for test case %d", idx)) - testutils.AssertEqual(t, usn, tc.usn, fmt.Sprintf("usn mismatch for test case %d", idx)) - testutils.AssertEqual(t, deleted, tc.deleted, fmt.Sprintf("deleted mismatch for test case %d", idx)) - testutils.AssertEqual(t, dirty, tc.dirty, fmt.Sprintf("dirty mismatch for test case %d", idx)) + assert.Equal(t, uuid, tc.uuid, fmt.Sprintf("uuid mismatch for test case %d", idx)) + assert.Equal(t, label, tc.label, fmt.Sprintf("label mismatch for test case %d", idx)) + assert.Equal(t, usn, tc.usn, fmt.Sprintf("usn mismatch for test case %d", idx)) + assert.Equal(t, deleted, tc.deleted, fmt.Sprintf("deleted mismatch for test case %d", idx)) + assert.Equal(t, dirty, tc.dirty, fmt.Sprintf("dirty mismatch for test case %d", idx)) }() } } @@ -627,8 +621,8 @@ func TestBookUpdate(t *testing.T) { for idx, tc := range testCases { func() { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := InitTestDB(t, "../tmp/dnote-test.db", nil) + defer CloseTestDB(t, db) b1 := Book{ UUID: "b1-uuid", @@ -645,9 +639,8 @@ func TestBookUpdate(t *testing.T) { Dirty: false, } - db := ctx.DB - testutils.MustExec(t, fmt.Sprintf("inserting b1 for test case %d", idx), db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b1.UUID, b1.Label, b1.USN, b1.Deleted, b1.Dirty) - testutils.MustExec(t, fmt.Sprintf("inserting b2 for test case %d", idx), db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b2.UUID, b2.Label, b2.USN, b2.Deleted, b2.Dirty) + MustExec(t, fmt.Sprintf("inserting b1 for test case %d", idx), db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b1.UUID, b1.Label, b1.USN, b1.Deleted, b1.Dirty) + MustExec(t, fmt.Sprintf("inserting b2 for test case %d", idx), db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b2.UUID, b2.Label, b2.USN, b2.Deleted, b2.Dirty) // execute tx, err := db.Begin() @@ -669,24 +662,24 @@ func TestBookUpdate(t *testing.T) { // test var b1Record, b2Record Book - testutils.MustScan(t, "getting b1", + MustScan(t, "getting b1", db.QueryRow("SELECT uuid, label, usn, deleted, dirty FROM books WHERE uuid = ?", tc.uuid), &b1Record.UUID, &b1Record.Label, &b1Record.USN, &b1Record.Deleted, &b1Record.Dirty) - testutils.MustScan(t, "getting b2", + MustScan(t, "getting b2", db.QueryRow("SELECT uuid, label, usn, deleted, dirty FROM books WHERE uuid = ?", b2.UUID), &b2Record.UUID, &b2Record.Label, &b2Record.USN, &b2Record.Deleted, &b2Record.Dirty) - testutils.AssertEqual(t, b1Record.UUID, b1.UUID, fmt.Sprintf("b1 uuid mismatch for test case %d", idx)) - testutils.AssertEqual(t, b1Record.Label, tc.newLabel, fmt.Sprintf("b1 label mismatch for test case %d", idx)) - testutils.AssertEqual(t, b1Record.USN, tc.newUSN, fmt.Sprintf("b1 usn mismatch for test case %d", idx)) - testutils.AssertEqual(t, b1Record.Deleted, tc.newDeleted, fmt.Sprintf("b1 deleted mismatch for test case %d", idx)) - testutils.AssertEqual(t, b1Record.Dirty, tc.newDirty, fmt.Sprintf("b1 dirty mismatch for test case %d", idx)) + assert.Equal(t, b1Record.UUID, b1.UUID, fmt.Sprintf("b1 uuid mismatch for test case %d", idx)) + assert.Equal(t, b1Record.Label, tc.newLabel, fmt.Sprintf("b1 label mismatch for test case %d", idx)) + assert.Equal(t, b1Record.USN, tc.newUSN, fmt.Sprintf("b1 usn mismatch for test case %d", idx)) + assert.Equal(t, b1Record.Deleted, tc.newDeleted, fmt.Sprintf("b1 deleted mismatch for test case %d", idx)) + assert.Equal(t, b1Record.Dirty, tc.newDirty, fmt.Sprintf("b1 dirty mismatch for test case %d", idx)) - testutils.AssertEqual(t, b2Record.UUID, b2.UUID, fmt.Sprintf("b2 uuid mismatch for test case %d", idx)) - testutils.AssertEqual(t, b2Record.Label, b2.Label, fmt.Sprintf("b2 label mismatch for test case %d", idx)) - testutils.AssertEqual(t, b2Record.USN, b2.USN, fmt.Sprintf("b2 usn mismatch for test case %d", idx)) - testutils.AssertEqual(t, b2Record.Deleted, b2.Deleted, fmt.Sprintf("b2 deleted mismatch for test case %d", idx)) - testutils.AssertEqual(t, b2Record.Dirty, b2.Dirty, fmt.Sprintf("b2 dirty mismatch for test case %d", idx)) + assert.Equal(t, b2Record.UUID, b2.UUID, fmt.Sprintf("b2 uuid mismatch for test case %d", idx)) + assert.Equal(t, b2Record.Label, b2.Label, fmt.Sprintf("b2 label mismatch for test case %d", idx)) + assert.Equal(t, b2Record.USN, b2.USN, fmt.Sprintf("b2 usn mismatch for test case %d", idx)) + assert.Equal(t, b2Record.Deleted, b2.Deleted, fmt.Sprintf("b2 deleted mismatch for test case %d", idx)) + assert.Equal(t, b2Record.Dirty, b2.Dirty, fmt.Sprintf("b2 dirty mismatch for test case %d", idx)) }() } } @@ -707,8 +700,8 @@ func TestBookUpdateUUID(t *testing.T) { t.Run(fmt.Sprintf("testCase%d", idx), func(t *testing.T) { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := InitTestDB(t, "../tmp/dnote-test.db", nil) + defer CloseTestDB(t, db) b1 := Book{ UUID: "b1-uuid", @@ -725,9 +718,8 @@ func TestBookUpdateUUID(t *testing.T) { Dirty: false, } - db := ctx.DB - testutils.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b1.UUID, b1.Label, b1.USN, b1.Deleted, b1.Dirty) - testutils.MustExec(t, "inserting b2", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b2.UUID, b2.Label, b2.USN, b2.Deleted, b2.Dirty) + MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b1.UUID, b1.Label, b1.USN, b1.Deleted, b1.Dirty) + MustExec(t, "inserting b2", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b2.UUID, b2.Label, b2.USN, b2.Deleted, b2.Dirty) // execute tx, err := db.Begin() @@ -743,24 +735,24 @@ func TestBookUpdateUUID(t *testing.T) { // test var b1Record, b2Record Book - testutils.MustScan(t, "getting b1", + MustScan(t, "getting b1", db.QueryRow("SELECT uuid, label, usn, deleted, dirty FROM books WHERE label = ?", "b1-label"), &b1Record.UUID, &b1Record.Label, &b1Record.USN, &b1Record.Deleted, &b1Record.Dirty) - testutils.MustScan(t, "getting b2", + MustScan(t, "getting b2", db.QueryRow("SELECT uuid, label, usn, deleted, dirty FROM books WHERE label = ?", "b2-label"), &b2Record.UUID, &b2Record.Label, &b2Record.USN, &b2Record.Deleted, &b2Record.Dirty) - testutils.AssertEqual(t, b1.UUID, tc.newUUID, "b1 original reference uuid mismatch") - testutils.AssertEqual(t, b1Record.UUID, tc.newUUID, "b1 uuid mismatch") - testutils.AssertEqual(t, b2Record.UUID, b2.UUID, "b2 uuid mismatch") + assert.Equal(t, b1.UUID, tc.newUUID, "b1 original reference uuid mismatch") + assert.Equal(t, b1Record.UUID, tc.newUUID, "b1 uuid mismatch") + assert.Equal(t, b2Record.UUID, b2.UUID, "b2 uuid mismatch") }) } } func TestBookExpunge(t *testing.T) { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := InitTestDB(t, "../tmp/dnote-test.db", nil) + defer CloseTestDB(t, db) b1 := Book{ UUID: "b1-uuid", @@ -777,9 +769,8 @@ func TestBookExpunge(t *testing.T) { Dirty: false, } - db := ctx.DB - testutils.MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b1.UUID, b1.Label, b1.USN, b1.Deleted, b1.Dirty) - testutils.MustExec(t, "inserting b2", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b2.UUID, b2.Label, b2.USN, b2.Deleted, b2.Dirty) + MustExec(t, "inserting b1", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b1.UUID, b1.Label, b1.USN, b1.Deleted, b1.Dirty) + MustExec(t, "inserting b2", db, "INSERT INTO books (uuid, label, usn, deleted, dirty) VALUES (?, ?, ?, ?, ?)", b2.UUID, b2.Label, b2.USN, b2.Deleted, b2.Dirty) // execute tx, err := db.Begin() @@ -796,27 +787,27 @@ func TestBookExpunge(t *testing.T) { // test var bookCount int - testutils.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) - testutils.AssertEqualf(t, bookCount, 1, "book count mismatch") + assert.Equalf(t, bookCount, 1, "book count mismatch") var b2Record Book - testutils.MustScan(t, "getting b2", + MustScan(t, "getting b2", db.QueryRow("SELECT uuid, label, usn, deleted, dirty FROM books WHERE uuid = ?", "b2-uuid"), &b2Record.UUID, &b2Record.Label, &b2Record.USN, &b2Record.Deleted, &b2Record.Dirty) - testutils.AssertEqual(t, b2Record.UUID, b2.UUID, "b2 uuid mismatch") - testutils.AssertEqual(t, b2Record.Label, b2.Label, "b2 label mismatch") - testutils.AssertEqual(t, b2Record.USN, b2.USN, "b2 usn mismatch") - testutils.AssertEqual(t, b2Record.Deleted, b2.Deleted, "b2 deleted mismatch") - testutils.AssertEqual(t, b2Record.Dirty, b2.Dirty, "b2 dirty mismatch") + assert.Equal(t, b2Record.UUID, b2.UUID, "b2 uuid mismatch") + assert.Equal(t, b2Record.Label, b2.Label, "b2 label mismatch") + assert.Equal(t, b2Record.USN, b2.USN, "b2 usn mismatch") + assert.Equal(t, b2Record.Deleted, b2.Deleted, "b2 deleted mismatch") + assert.Equal(t, b2Record.Dirty, b2.Dirty, "b2 dirty mismatch") } // TestNoteFTS tests that note full text search indices stay in sync with the notes after insert, update and delete func TestNoteFTS(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := InitTestDB(t, "../tmp/dnote-test.db", nil) + defer CloseTestDB(t, db) // execute - insert n := Note{ @@ -830,7 +821,6 @@ func TestNoteFTS(t *testing.T) { Deleted: false, Dirty: false, } - db := ctx.DB tx, err := db.Begin() if err != nil { @@ -846,13 +836,13 @@ func TestNoteFTS(t *testing.T) { // test var noteCount, noteFtsCount, noteSearchCount int - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting note_fts", db.QueryRow("SELECT count(*) FROM note_fts"), ¬eFtsCount) - testutils.MustScan(t, "counting search results", db.QueryRow("SELECT count(*) FROM note_fts WHERE note_fts MATCH ?", "foo"), ¬eSearchCount) + MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + MustScan(t, "counting note_fts", db.QueryRow("SELECT count(*) FROM note_fts"), ¬eFtsCount) + MustScan(t, "counting search results", db.QueryRow("SELECT count(*) FROM note_fts WHERE note_fts MATCH ?", "foo"), ¬eSearchCount) - testutils.AssertEqual(t, noteCount, 1, "noteCount mismatch") - testutils.AssertEqual(t, noteFtsCount, 1, "noteFtsCount mismatch") - testutils.AssertEqual(t, noteSearchCount, 1, "noteSearchCount mismatch") + assert.Equal(t, noteCount, 1, "noteCount mismatch") + assert.Equal(t, noteFtsCount, 1, "noteFtsCount mismatch") + assert.Equal(t, noteSearchCount, 1, "noteSearchCount mismatch") // execute - update tx, err = db.Begin() @@ -869,15 +859,15 @@ func TestNoteFTS(t *testing.T) { tx.Commit() // test - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting note_fts", db.QueryRow("SELECT count(*) FROM note_fts"), ¬eFtsCount) - testutils.AssertEqual(t, noteCount, 1, "noteCount mismatch") - testutils.AssertEqual(t, noteFtsCount, 1, "noteFtsCount mismatch") + MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + MustScan(t, "counting note_fts", db.QueryRow("SELECT count(*) FROM note_fts"), ¬eFtsCount) + assert.Equal(t, noteCount, 1, "noteCount mismatch") + assert.Equal(t, noteFtsCount, 1, "noteFtsCount mismatch") - testutils.MustScan(t, "counting search results", db.QueryRow("SELECT count(*) FROM note_fts WHERE note_fts MATCH ?", "foo"), ¬eSearchCount) - testutils.AssertEqual(t, noteSearchCount, 0, "noteSearchCount for foo mismatch") - testutils.MustScan(t, "counting search results", db.QueryRow("SELECT count(*) FROM note_fts WHERE note_fts MATCH ?", "baz"), ¬eSearchCount) - testutils.AssertEqual(t, noteSearchCount, 1, "noteSearchCount for baz mismatch") + MustScan(t, "counting search results", db.QueryRow("SELECT count(*) FROM note_fts WHERE note_fts MATCH ?", "foo"), ¬eSearchCount) + assert.Equal(t, noteSearchCount, 0, "noteSearchCount for foo mismatch") + MustScan(t, "counting search results", db.QueryRow("SELECT count(*) FROM note_fts WHERE note_fts MATCH ?", "baz"), ¬eSearchCount) + assert.Equal(t, noteSearchCount, 1, "noteSearchCount for baz mismatch") // execute - delete tx, err = db.Begin() @@ -893,9 +883,9 @@ func TestNoteFTS(t *testing.T) { tx.Commit() // test - testutils.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) - testutils.MustScan(t, "counting note_fts", db.QueryRow("SELECT count(*) FROM note_fts"), ¬eFtsCount) + MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + MustScan(t, "counting note_fts", db.QueryRow("SELECT count(*) FROM note_fts"), ¬eFtsCount) - testutils.AssertEqual(t, noteCount, 0, "noteCount mismatch") - testutils.AssertEqual(t, noteFtsCount, 0, "noteFtsCount mismatch") + assert.Equal(t, noteCount, 0, "noteCount mismatch") + assert.Equal(t, noteFtsCount, 0, "noteFtsCount mismatch") } diff --git a/cli/core/operations.go b/pkg/cli/database/queries.go similarity index 59% rename from cli/core/operations.go rename to pkg/cli/database/queries.go index 73ae8401..661b06eb 100644 --- a/cli/core/operations.go +++ b/pkg/cli/database/queries.go @@ -16,17 +16,25 @@ * along with Dnote CLI. If not, see . */ -package core +package database import ( - "encoding/base64" + "database/sql" - "github.com/dnote/dnote/cli/infra" "github.com/pkg/errors" ) +// GetSystem scans the given system configuration record onto the destination +func GetSystem(db *DB, key string, dest interface{}) error { + if err := db.QueryRow("SELECT value FROM system WHERE key = ?", key).Scan(dest); err != nil { + return errors.Wrap(err, "finding system configuration record") + } + + return nil +} + // InsertSystem inserets a system configuration -func InsertSystem(db *infra.DB, key, val string) error { +func InsertSystem(db *DB, key, val string) error { if _, err := db.Exec("INSERT INTO system (key, value) VALUES (? , ?);", key, val); err != nil { return errors.Wrap(err, "saving system config") } @@ -35,7 +43,7 @@ func InsertSystem(db *infra.DB, key, val string) error { } // UpsertSystem inserts or updates a system configuration -func UpsertSystem(db *infra.DB, key, val string) error { +func UpsertSystem(db *DB, key, val string) error { var count int if err := db.QueryRow("SELECT count(*) FROM system WHERE key = ?", key).Scan(&count); err != nil { return errors.Wrap(err, "counting system record") @@ -55,7 +63,7 @@ func UpsertSystem(db *infra.DB, key, val string) error { } // UpdateSystem updates a system configuration -func UpdateSystem(db *infra.DB, key, val interface{}) error { +func UpdateSystem(db *DB, key, val interface{}) error { if _, err := db.Exec("UPDATE system SET value = ? WHERE key = ?", val, key); err != nil { return errors.Wrap(err, "updating system config") } @@ -63,17 +71,8 @@ func UpdateSystem(db *infra.DB, key, val interface{}) error { return nil } -// GetSystem scans the given system configuration record onto the destination -func GetSystem(db *infra.DB, key string, dest interface{}) error { - if err := db.QueryRow("SELECT value FROM system WHERE key = ?", key).Scan(dest); err != nil { - return errors.Wrap(err, "finding system configuration record") - } - - return nil -} - // DeleteSystem delets the given system record -func DeleteSystem(db *infra.DB, key string) error { +func DeleteSystem(db *DB, key string) error { if _, err := db.Exec("DELETE FROM system WHERE key = ?", key); err != nil { return errors.Wrap(err, "deleting system config") } @@ -81,23 +80,44 @@ func DeleteSystem(db *infra.DB, key string) error { return nil } -// GetCipherKey retrieves the cipher key and decode the base64 into bytes. -func GetCipherKey(ctx infra.DnoteCtx) ([]byte, error) { - db, err := ctx.DB.Begin() - if err != nil { - return nil, errors.Wrap(err, "beginning transaction") - } - - var cipherKeyB64 string - err = GetSystem(db, infra.SystemCipherKey, &cipherKeyB64) - if err != nil { - return []byte{}, errors.Wrap(err, "getting enc key") - } - - cipherKey, err := base64.StdEncoding.DecodeString(cipherKeyB64) - if err != nil { - return nil, errors.Wrap(err, "decoding cipherKey from base64") - } - - return cipherKey, nil +// NoteInfo is a basic information about a note +type NoteInfo struct { + RowID int + BookLabel string + UUID string + Content string + AddedOn int64 + EditedOn int64 +} + +// GetNoteInfo returns a NoteInfo for the note with the given noteRowID +func GetNoteInfo(db *DB, noteRowID string) (NoteInfo, error) { + var ret NoteInfo + + err := db.QueryRow(`SELECT books.label, notes.uuid, notes.body, notes.added_on, notes.edited_on, notes.rowid + FROM notes + INNER JOIN books ON books.uuid = notes.book_uuid + WHERE notes.rowid = ? AND notes.deleted = false`, noteRowID). + Scan(&ret.BookLabel, &ret.UUID, &ret.Content, &ret.AddedOn, &ret.EditedOn, &ret.RowID) + if err == sql.ErrNoRows { + return ret, errors.Errorf("note %s not found", noteRowID) + } else if err != nil { + return ret, errors.Wrap(err, "querying the note") + } + + return ret, nil + +} + +// GetBookUUID returns a uuid of a book given a label +func GetBookUUID(db *DB, label string) (string, error) { + var ret string + err := db.QueryRow("SELECT uuid FROM books WHERE label = ?", label).Scan(&ret) + if err == sql.ErrNoRows { + return ret, errors.Errorf("book '%s' not found", label) + } else if err != nil { + return ret, errors.Wrap(err, "querying the book") + } + + return ret, nil } diff --git a/cli/core/operations_test.go b/pkg/cli/database/queries_test.go similarity index 62% rename from cli/core/operations_test.go rename to pkg/cli/database/queries_test.go index 82c742ab..cfcff61f 100644 --- a/cli/core/operations_test.go +++ b/pkg/cli/database/queries_test.go @@ -16,13 +16,13 @@ * along with Dnote CLI. If not, see . */ -package core +package database import ( "fmt" "testing" - "github.com/dnote/dnote/cli/testutils" + "github.com/dnote/dnote/pkg/assert" "github.com/pkg/errors" ) @@ -44,12 +44,10 @@ func TestInsertSystem(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("insert %s %s", tc.key, tc.val), func(t *testing.T) { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := InitTestDB(t, "../tmp/dnote-test.db", nil) + defer CloseTestDB(t, db) // execute - db := ctx.DB - tx, err := db.Begin() if err != nil { t.Fatalf(errors.Wrap(err, "beginning a transaction").Error()) @@ -64,11 +62,11 @@ func TestInsertSystem(t *testing.T) { // test var key, val string - testutils.MustScan(t, "getting the saved record", + MustScan(t, "getting the saved record", db.QueryRow("SELECT key, value FROM system WHERE key = ?", tc.key), &key, &val) - testutils.AssertEqual(t, key, tc.key, "key mismatch for test case") - testutils.AssertEqual(t, val, tc.val, "val mismatch for test case") + assert.Equal(t, key, tc.key, "key mismatch for test case") + assert.Equal(t, val, tc.val, "val mismatch for test case") }) } } @@ -94,14 +92,13 @@ func TestUpsertSystem(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("insert %s %s", tc.key, tc.val), func(t *testing.T) { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := InitTestDB(t, "../tmp/dnote-test.db", nil) + defer CloseTestDB(t, db) - db := ctx.DB - testutils.MustExec(t, "inserting a system configuration", db, "INSERT INTO system (key, value) VALUES (?, ?)", "baz", "quz") + MustExec(t, "inserting a system configuration", db, "INSERT INTO system (key, value) VALUES (?, ?)", "baz", "quz") var initialSystemCount int - testutils.MustScan(t, "counting records", db.QueryRow("SELECT count(*) FROM system"), &initialSystemCount) + MustScan(t, "counting records", db.QueryRow("SELECT count(*) FROM system"), &initialSystemCount) // execute tx, err := db.Begin() @@ -118,15 +115,15 @@ func TestUpsertSystem(t *testing.T) { // test var key, val string - testutils.MustScan(t, "getting the saved record", + MustScan(t, "getting the saved record", db.QueryRow("SELECT key, value FROM system WHERE key = ?", tc.key), &key, &val) var systemCount int - testutils.MustScan(t, "counting records", + MustScan(t, "counting records", db.QueryRow("SELECT count(*) FROM system"), &systemCount) - testutils.AssertEqual(t, key, tc.key, "key mismatch") - testutils.AssertEqual(t, val, tc.val, "val mismatch") - testutils.AssertEqual(t, systemCount, initialSystemCount+tc.countDelta, "count mismatch") + assert.Equal(t, key, tc.key, "key mismatch") + assert.Equal(t, val, tc.val, "val mismatch") + assert.Equal(t, systemCount, initialSystemCount+tc.countDelta, "count mismatch") }) } } @@ -134,12 +131,11 @@ func TestUpsertSystem(t *testing.T) { func TestGetSystem(t *testing.T) { t.Run(fmt.Sprintf("get string value"), func(t *testing.T) { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := InitTestDB(t, "../tmp/dnote-test.db", nil) + defer CloseTestDB(t, db) // execute - db := ctx.DB - testutils.MustExec(t, "inserting a system configuration", db, "INSERT INTO system (key, value) VALUES (?, ?)", "foo", "bar") + MustExec(t, "inserting a system configuration", db, "INSERT INTO system (key, value) VALUES (?, ?)", "foo", "bar") tx, err := db.Begin() if err != nil { @@ -153,17 +149,16 @@ func TestGetSystem(t *testing.T) { tx.Commit() // test - testutils.AssertEqual(t, dest, "bar", "dest mismatch") + assert.Equal(t, dest, "bar", "dest mismatch") }) t.Run(fmt.Sprintf("get int64 value"), func(t *testing.T) { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := InitTestDB(t, "../tmp/dnote-test.db", nil) + defer CloseTestDB(t, db) // execute - db := ctx.DB - testutils.MustExec(t, "inserting a system configuration", db, "INSERT INTO system (key, value) VALUES (?, ?)", "foo", 1234) + MustExec(t, "inserting a system configuration", db, "INSERT INTO system (key, value) VALUES (?, ?)", "foo", 1234) tx, err := db.Begin() if err != nil { @@ -177,7 +172,7 @@ func TestGetSystem(t *testing.T) { tx.Commit() // test - testutils.AssertEqual(t, dest, int64(1234), "dest mismatch") + assert.Equal(t, dest, int64(1234), "dest mismatch") }) } @@ -200,15 +195,14 @@ func TestUpdateSystem(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("update %s %s", tc.key, tc.val), func(t *testing.T) { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := InitTestDB(t, "../tmp/dnote-test.db", nil) + defer CloseTestDB(t, db) - db := ctx.DB - testutils.MustExec(t, "inserting a system configuration", db, "INSERT INTO system (key, value) VALUES (?, ?)", "foo", "fuz") - testutils.MustExec(t, "inserting a system configuration", db, "INSERT INTO system (key, value) VALUES (?, ?)", "baz", "quz") + MustExec(t, "inserting a system configuration", db, "INSERT INTO system (key, value) VALUES (?, ?)", "foo", "fuz") + MustExec(t, "inserting a system configuration", db, "INSERT INTO system (key, value) VALUES (?, ?)", "baz", "quz") var initialSystemCount int - testutils.MustScan(t, "counting records", db.QueryRow("SELECT count(*) FROM system"), &initialSystemCount) + MustScan(t, "counting records", db.QueryRow("SELECT count(*) FROM system"), &initialSystemCount) // execute tx, err := db.Begin() @@ -225,15 +219,15 @@ func TestUpdateSystem(t *testing.T) { // test var key, val string - testutils.MustScan(t, "getting the saved record", + MustScan(t, "getting the saved record", db.QueryRow("SELECT key, value FROM system WHERE key = ?", tc.key), &key, &val) var systemCount int - testutils.MustScan(t, "counting records", + MustScan(t, "counting records", db.QueryRow("SELECT count(*) FROM system"), &systemCount) - testutils.AssertEqual(t, key, tc.key, "key mismatch") - testutils.AssertEqual(t, val, tc.val, "val mismatch") - testutils.AssertEqual(t, systemCount, initialSystemCount, "count mismatch") + assert.Equal(t, key, tc.key, "key mismatch") + assert.Equal(t, val, tc.val, "val mismatch") + assert.Equal(t, systemCount, initialSystemCount, "count mismatch") }) } } diff --git a/cli/infra/sql.go b/pkg/cli/database/sql.go similarity index 90% rename from cli/infra/sql.go rename to pkg/cli/database/sql.go index 17327e27..0de44c2e 100644 --- a/cli/infra/sql.go +++ b/pkg/cli/database/sql.go @@ -16,12 +16,14 @@ * along with Dnote CLI. If not, see . */ -package infra +package database import ( "database/sql" "github.com/pkg/errors" + // use sqlite + _ "github.com/mattn/go-sqlite3" ) // SQLCommon is the minimal interface required by a db connection @@ -45,27 +47,8 @@ type sqlTx interface { // DB contains information about the current database connection type DB struct { - Conn SQLCommon -} - -// OpenDB initializes a new connection to the sqlite database -func OpenDB(dbPath string) (*DB, error) { - dbConn, err := sql.Open("sqlite3", dbPath) - if err != nil { - return nil, errors.Wrap(err, "opening db connection") - } - - // Send a ping to ensure that the connection is established - // if err := dbConn.Ping(); err != nil { - // dbConn.Close() - // return nil, errors.Wrap(err, "ping") - // } - - db := &DB{ - Conn: dbConn, - } - - return db, nil + Conn SQLCommon + Filepath string } // Begin begins a transaction @@ -136,3 +119,18 @@ func (d *DB) Close() error { return errors.New("can't close db") } + +// Open initializes a new connection to the sqlite database +func Open(dbPath string) (*DB, error) { + dbConn, err := sql.Open("sqlite3", dbPath) + if err != nil { + return nil, errors.Wrap(err, "opening db connection") + } + + db := &DB{ + Conn: dbConn, + Filepath: dbPath, + } + + return db, nil +} diff --git a/pkg/cli/database/testutils.go b/pkg/cli/database/testutils.go new file mode 100644 index 00000000..2e36b40e --- /dev/null +++ b/pkg/cli/database/testutils.go @@ -0,0 +1,170 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +package database + +import ( + "database/sql" + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/dnote/dnote/pkg/cli/consts" + "github.com/dnote/dnote/pkg/cli/utils" + "github.com/pkg/errors" +) + +var defaultSchemaSQL = `CREATE TABLE books + ( + uuid text PRIMARY KEY, + label text NOT NULL + , dirty bool DEFAULT false, usn int DEFAULT 0 NOT NULL, deleted bool DEFAULT false); +CREATE TABLE system + ( + key string NOT NULL, + value text NOT NULL + ); +CREATE UNIQUE INDEX idx_books_label ON books(label); +CREATE UNIQUE INDEX idx_books_uuid ON books(uuid); +CREATE TABLE IF NOT EXISTS "notes" + ( + uuid text NOT NULL, + book_uuid text NOT NULL, + body text NOT NULL, + added_on integer NOT NULL, + edited_on integer DEFAULT 0, + public bool DEFAULT false, + dirty bool DEFAULT false, + usn int DEFAULT 0 NOT NULL, + deleted bool DEFAULT false + ); +CREATE VIRTUAL TABLE note_fts USING fts5(content=notes, body, tokenize="porter unicode61 categories 'L* N* Co Ps Pe'") +/* note_fts(body) */; +CREATE TABLE IF NOT EXISTS 'note_fts_data'(id INTEGER PRIMARY KEY, block BLOB); +CREATE TABLE IF NOT EXISTS 'note_fts_idx'(segid, term, pgno, PRIMARY KEY(segid, term)) WITHOUT ROWID; +CREATE TABLE IF NOT EXISTS 'note_fts_docsize'(id INTEGER PRIMARY KEY, sz BLOB); +CREATE TABLE IF NOT EXISTS 'note_fts_config'(k PRIMARY KEY, v) WITHOUT ROWID; +CREATE TRIGGER notes_after_insert AFTER INSERT ON notes BEGIN + INSERT INTO note_fts(rowid, body) VALUES (new.rowid, new.body); + END; +CREATE TRIGGER notes_after_delete AFTER DELETE ON notes BEGIN + INSERT INTO note_fts(note_fts, rowid, body) VALUES ('delete', old.rowid, old.body); + END; +CREATE TRIGGER notes_after_update AFTER UPDATE ON notes BEGIN + INSERT INTO note_fts(note_fts, rowid, body) VALUES ('delete', old.rowid, old.body); + INSERT INTO note_fts(rowid, body) VALUES (new.rowid, new.body); + END; +CREATE TABLE actions + ( + uuid text PRIMARY KEY, + schema integer NOT NULL, + type text NOT NULL, + data text NOT NULL, + timestamp integer NOT NULL + ); +CREATE UNIQUE INDEX idx_notes_uuid ON notes(uuid); +CREATE INDEX idx_notes_book_uuid ON notes(book_uuid);` + +// MustScan scans the given row and fails a test in case of any errors +func MustScan(t *testing.T, message string, row *sql.Row, args ...interface{}) { + err := row.Scan(args...) + if err != nil { + t.Fatal(errors.Wrap(errors.Wrap(err, "scanning a row"), message)) + } +} + +// MustExec executes the given SQL query and fails a test if an error occurs +func MustExec(t *testing.T, message string, db *DB, query string, args ...interface{}) sql.Result { + result, err := db.Exec(query, args...) + if err != nil { + t.Fatal(errors.Wrap(errors.Wrap(err, "executing sql"), message)) + } + + return result +} + +// TestDBOptions contains options for test database +type TestDBOptions struct { + SchemaSQLPath string + SkipMigration bool +} + +// InitTestDB opens a test database connection +func InitTestDB(t *testing.T, dbPath string, options *TestDBOptions) *DB { + db, err := Open(dbPath) + if err != nil { + t.Fatal(errors.Wrap(err, "opening database connection")) + } + + dir, _ := filepath.Split(dbPath) + err = os.MkdirAll(dir, 0777) + if err != nil { + t.Fatal(errors.Wrap(err, "creating the directory for test database file")) + } + + var schemaSQL string + if options != nil && options.SchemaSQLPath != "" { + b := utils.ReadFileAbs(options.SchemaSQLPath) + schemaSQL = string(b) + } else { + schemaSQL = defaultSchemaSQL + } + + if _, err := db.Exec(schemaSQL); err != nil { + t.Fatal(errors.Wrap(err, "running schema sql")) + } + + if options == nil || !options.SkipMigration { + MarkMigrationComplete(t, db) + } + + return db +} + +// CloseTestDB closes the test database +func CloseTestDB(t *testing.T, db *DB) { + if err := db.Close(); err != nil { + t.Fatal(errors.Wrap(err, "closing database")) + } + + if err := os.RemoveAll(db.Filepath); err != nil { + t.Fatal(errors.Wrap(err, "removing database file")) + } +} + +// OpenTestDB opens the database connection to the test database +func OpenTestDB(t *testing.T, dnoteDir string) *DB { + dbPath := fmt.Sprintf("%s/%s", dnoteDir, consts.DnoteDBFileName) + db, err := Open(dbPath) + if err != nil { + t.Fatal(errors.Wrap(err, "opening database connection to the test database")) + } + + return db +} + +// MarkMigrationComplete marks all migrations as complete in the database +func MarkMigrationComplete(t *testing.T, db *DB) { + if _, err := db.Exec("INSERT INTO system (key, value) VALUES (? , ?);", consts.SystemSchema, 11); err != nil { + t.Fatal(errors.Wrap(err, "inserting schema")) + } + if _, err := db.Exec("INSERT INTO system (key, value) VALUES (? , ?);", consts.SystemRemoteSchema, 1); err != nil { + t.Fatal(errors.Wrap(err, "inserting remote schema")) + } +} diff --git a/cli/dnote-completion.bash b/pkg/cli/dnote-completion.bash similarity index 100% rename from cli/dnote-completion.bash rename to pkg/cli/dnote-completion.bash diff --git a/cli/dnote-completion.zsh b/pkg/cli/dnote-completion.zsh similarity index 100% rename from cli/dnote-completion.zsh rename to pkg/cli/dnote-completion.zsh diff --git a/pkg/cli/infra/config.go b/pkg/cli/infra/config.go new file mode 100644 index 00000000..83d30d64 --- /dev/null +++ b/pkg/cli/infra/config.go @@ -0,0 +1,56 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +package infra + +import ( + "fmt" + "io/ioutil" + + "github.com/dnote/dnote/pkg/cli/context" + "github.com/pkg/errors" + "gopkg.in/yaml.v2" +) + +var ( + // ConfigFilename is the name of the config file + ConfigFilename = "dnoterc" +) + +// GetConfigPath returns the path to the dnote config file +func GetConfigPath(ctx context.DnoteCtx) string { + return fmt.Sprintf("%s/%s", ctx.DnoteDir, ConfigFilename) +} + +// ReadConfig reads the config file +func ReadConfig(ctx context.DnoteCtx) (Config, error) { + var ret Config + + configPath := GetConfigPath(ctx) + b, err := ioutil.ReadFile(configPath) + if err != nil { + return ret, errors.Wrap(err, "reading config file") + } + + err = yaml.Unmarshal(b, &ret) + if err != nil { + return ret, errors.Wrap(err, "unmarshalling config") + } + + return ret, nil +} diff --git a/pkg/cli/infra/init.go b/pkg/cli/infra/init.go new file mode 100644 index 00000000..b9eb54b5 --- /dev/null +++ b/pkg/cli/infra/init.go @@ -0,0 +1,365 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +// Package infra provides operations and definitions for the +// local infrastructure for Dnote +package infra + +import ( + "database/sql" + "encoding/base64" + "fmt" + "io/ioutil" + "os" + "os/user" + "strconv" + "time" + + "github.com/dnote/dnote/pkg/cli/consts" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/log" + "github.com/dnote/dnote/pkg/cli/migrate" + "github.com/dnote/dnote/pkg/cli/utils" + "github.com/pkg/errors" + "github.com/spf13/cobra" + "gopkg.in/yaml.v2" +) + +// Config holds dnote configuration +type Config struct { + Editor string +} + +// RunEFunc is a function type of dnote commands +type RunEFunc func(*cobra.Command, []string) error + +func newCtx(apiEndpoint, versionTag string) (context.DnoteCtx, error) { + homeDir, err := getHomeDir() + if err != nil { + return context.DnoteCtx{}, errors.Wrap(err, "Failed to get home dir") + } + dnoteDir := getDnoteDir(homeDir) + + dnoteDBPath := fmt.Sprintf("%s/%s", dnoteDir, consts.DnoteDBFileName) + db, err := database.Open(dnoteDBPath) + if err != nil { + return context.DnoteCtx{}, errors.Wrap(err, "conntecting to db") + } + + ctx := context.DnoteCtx{ + HomeDir: homeDir, + DnoteDir: dnoteDir, + APIEndpoint: apiEndpoint, + Version: versionTag, + DB: db, + } + + return ctx, nil +} + +// Init initializes the Dnote environment and returns a new dnote context +func Init(apiEndpoint, versionTag string) (*context.DnoteCtx, error) { + ctx, err := newCtx(apiEndpoint, versionTag) + if err != nil { + return nil, errors.Wrap(err, "initializing a context") + } + + if err := InitFiles(ctx); err != nil { + return nil, errors.Wrap(err, "initializing files") + } + + if err := InitDB(ctx); err != nil { + return nil, errors.Wrap(err, "initializing database") + } + if err := InitSystem(ctx); err != nil { + return nil, errors.Wrap(err, "initializing system data") + } + + if err := migrate.Legacy(ctx); err != nil { + return nil, errors.Wrap(err, "running legacy migration") + } + if err := migrate.Run(ctx, migrate.LocalSequence, migrate.LocalMode); err != nil { + return nil, errors.Wrap(err, "running migration") + } + + ctx, err = SetupCtx(ctx) + if err != nil { + return nil, errors.Wrap(err, "setting up the context") + } + + return &ctx, nil +} + +// SetupCtx populates context and returns a new context +func SetupCtx(ctx context.DnoteCtx) (context.DnoteCtx, error) { + db := ctx.DB + + var sessionKey, cipherKeyB64 string + var sessionKeyExpiry int64 + + err := db.QueryRow("SELECT value FROM system WHERE key = ?", consts.SystemSessionKey).Scan(&sessionKey) + if err != nil && err != sql.ErrNoRows { + return ctx, errors.Wrap(err, "finding sesison key") + } + err = db.QueryRow("SELECT value FROM system WHERE key = ?", consts.SystemCipherKey).Scan(&cipherKeyB64) + if err != nil && err != sql.ErrNoRows { + return ctx, errors.Wrap(err, "finding sesison key") + } + err = db.QueryRow("SELECT value FROM system WHERE key = ?", consts.SystemSessionKeyExpiry).Scan(&sessionKeyExpiry) + if err != nil && err != sql.ErrNoRows { + return ctx, errors.Wrap(err, "finding sesison key expiry") + } + + cipherKey, err := base64.StdEncoding.DecodeString(cipherKeyB64) + if err != nil { + return ctx, errors.Wrap(err, "decoding cipherKey from base64") + } + + ret := context.DnoteCtx{ + HomeDir: ctx.HomeDir, + DnoteDir: ctx.DnoteDir, + APIEndpoint: ctx.APIEndpoint, + Version: ctx.Version, + DB: ctx.DB, + SessionKey: sessionKey, + SessionKeyExpiry: sessionKeyExpiry, + CipherKey: cipherKey, + } + + return ret, nil +} + +func getDnoteDir(homeDir string) string { + var ret string + + dnoteDirEnv := os.Getenv("DNOTE_DIR") + if dnoteDirEnv == "" { + ret = fmt.Sprintf("%s/%s", homeDir, consts.DnoteDirName) + } else { + ret = dnoteDirEnv + } + + return ret +} + +func getHomeDir() (string, error) { + homeDirEnv := os.Getenv("DNOTE_HOME_DIR") + if homeDirEnv != "" { + return homeDirEnv, nil + } + + usr, err := user.Current() + if err != nil { + return "", errors.Wrap(err, "Failed to get current user") + } + + return usr.HomeDir, nil +} + +// InitDB initializes the database. +// Ideally this process must be a part of migration sequence. But it is performed +// seaprately because it is a prerequisite for legacy migration. +func InitDB(ctx context.DnoteCtx) error { + log.Debug("initializing the database\n") + + db := ctx.DB + + _, err := db.Exec(`CREATE TABLE IF NOT EXISTS notes + ( + id integer PRIMARY KEY AUTOINCREMENT, + uuid text NOT NULL, + book_uuid text NOT NULL, + content text NOT NULL, + added_on integer NOT NULL, + edited_on integer DEFAULT 0, + public bool DEFAULT false + )`) + if err != nil { + return errors.Wrap(err, "creating notes table") + } + + _, err = db.Exec(`CREATE TABLE IF NOT EXISTS books + ( + uuid text PRIMARY KEY, + label text NOT NULL + )`) + if err != nil { + return errors.Wrap(err, "creating books table") + } + + _, err = db.Exec(`CREATE TABLE IF NOT EXISTS system + ( + key string NOT NULL, + value text NOT NULL + )`) + if err != nil { + return errors.Wrap(err, "creating system table") + } + + _, err = db.Exec(`CREATE TABLE IF NOT EXISTS actions + ( + uuid text PRIMARY KEY, + schema integer NOT NULL, + type text NOT NULL, + data text NOT NULL, + timestamp integer NOT NULL + )`) + if err != nil { + return errors.Wrap(err, "creating actions table") + } + + _, err = db.Exec(` + CREATE UNIQUE INDEX IF NOT EXISTS idx_books_label ON books(label); + CREATE UNIQUE INDEX IF NOT EXISTS idx_notes_uuid ON notes(uuid); + CREATE UNIQUE INDEX IF NOT EXISTS idx_books_uuid ON books(uuid); + CREATE INDEX IF NOT EXISTS idx_notes_book_uuid ON notes(book_uuid);`) + if err != nil { + return errors.Wrap(err, "creating indices") + } + + return nil +} + +func initSystemKV(db *database.DB, key string, val string) error { + var count int + if err := db.QueryRow("SELECT count(*) FROM system WHERE key = ?", key).Scan(&count); err != nil { + return errors.Wrapf(err, "counting %s", key) + } + + if count > 0 { + return nil + } + + if _, err := db.Exec("INSERT INTO system (key, value) VALUES (?, ?)", key, val); err != nil { + db.Rollback() + return errors.Wrapf(err, "inserting %s %s", key, val) + } + + return nil +} + +// InitSystem inserts system data if missing +func InitSystem(ctx context.DnoteCtx) error { + log.Debug("initializing the system\n") + + db := ctx.DB + + tx, err := db.Begin() + if err != nil { + return errors.Wrap(err, "beginning a transaction") + } + + nowStr := strconv.FormatInt(time.Now().Unix(), 10) + if err := initSystemKV(tx, consts.SystemLastUpgrade, nowStr); err != nil { + return errors.Wrapf(err, "initializing system config for %s", consts.SystemLastUpgrade) + } + if err := initSystemKV(tx, consts.SystemLastMaxUSN, "0"); err != nil { + return errors.Wrapf(err, "initializing system config for %s", consts.SystemLastMaxUSN) + } + if err := initSystemKV(tx, consts.SystemLastSyncAt, "0"); err != nil { + return errors.Wrapf(err, "initializing system config for %s", consts.SystemLastSyncAt) + } + + tx.Commit() + + return nil +} + +// getEditorCommand returns the system's editor command with appropriate flags, +// if necessary, to make the command wait until editor is close to exit. +func getEditorCommand() string { + editor := os.Getenv("EDITOR") + + var ret string + + switch editor { + case "atom": + ret = "atom -w" + case "subl": + ret = "subl -n -w" + case "mate": + ret = "mate -w" + case "vim": + ret = "vim" + case "nano": + ret = "nano" + case "emacs": + ret = "emacs" + case "nvim": + ret = "nvim" + default: + ret = "vi" + } + + return ret +} + +// initDnoteDir initializes dnote directory if it does not exist yet +func initDnoteDir(ctx context.DnoteCtx) error { + path := ctx.DnoteDir + + if utils.FileExists(path) { + return nil + } + + if err := os.MkdirAll(path, 0755); err != nil { + return errors.Wrap(err, "Failed to create dnote directory") + } + + return nil +} + +// initConfigFile populates a new config file if it does not exist yet +func initConfigFile(ctx context.DnoteCtx) error { + path := GetConfigPath(ctx) + + if utils.FileExists(path) { + return nil + } + + editor := getEditorCommand() + + config := Config{ + Editor: editor, + } + + b, err := yaml.Marshal(config) + if err != nil { + return errors.Wrap(err, "marshalling config into YAML") + } + + err = ioutil.WriteFile(path, b, 0644) + if err != nil { + return errors.Wrap(err, "writing the config file") + } + + return nil +} + +// InitFiles creates, if necessary, the dnote directory and files inside +func InitFiles(ctx context.DnoteCtx) error { + if err := initDnoteDir(ctx); err != nil { + return errors.Wrap(err, "creating the dnote dir") + } + if err := initConfigFile(ctx); err != nil { + return errors.Wrap(err, "generating the config file") + } + + return nil +} diff --git a/cli/core/core_test.go b/pkg/cli/infra/init_test.go similarity index 57% rename from cli/core/core_test.go rename to pkg/cli/infra/init_test.go index 2d084e86..f5a15e48 100644 --- a/cli/core/core_test.go +++ b/pkg/cli/infra/init_test.go @@ -16,24 +16,23 @@ * along with Dnote CLI. If not, see . */ -package core +package infra import ( "testing" - "github.com/dnote/dnote/cli/testutils" + "github.com/dnote/dnote/pkg/assert" + "github.com/dnote/dnote/pkg/cli/database" "github.com/pkg/errors" ) func TestInitSystemKV(t *testing.T) { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) - - db := ctx.DB + db := database.InitTestDB(t, "../tmp/dnote-test.db", nil) + defer database.CloseTestDB(t, db) var originalCount int - testutils.MustScan(t, "counting system configs", db.QueryRow("SELECT count(*) FROM system"), &originalCount) + database.MustScan(t, "counting system configs", db.QueryRow("SELECT count(*) FROM system"), &originalCount) // Execute tx, err := db.Begin() @@ -50,25 +49,24 @@ func TestInitSystemKV(t *testing.T) { // Test var count int - testutils.MustScan(t, "counting system configs", db.QueryRow("SELECT count(*) FROM system"), &count) - testutils.AssertEqual(t, count, originalCount+1, "system count mismatch") + database.MustScan(t, "counting system configs", db.QueryRow("SELECT count(*) FROM system"), &count) + assert.Equal(t, count, originalCount+1, "system count mismatch") var val string - testutils.MustScan(t, "getting system value", + database.MustScan(t, "getting system value", db.QueryRow("SELECT value FROM system WHERE key = ?", "testKey"), &val) - testutils.AssertEqual(t, val, "testVal", "system value mismatch") + assert.Equal(t, val, "testVal", "system value mismatch") } func TestInitSystemKV_existing(t *testing.T) { // Setup - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + db := database.InitTestDB(t, "../tmp/dnote-test.db", nil) + defer database.CloseTestDB(t, db) - db := ctx.DB - testutils.MustExec(t, "inserting a system config", db, "INSERT INTO system (key, value) VALUES (?, ?)", "testKey", "testVal") + database.MustExec(t, "inserting a system config", db, "INSERT INTO system (key, value) VALUES (?, ?)", "testKey", "testVal") var originalCount int - testutils.MustScan(t, "counting system configs", db.QueryRow("SELECT count(*) FROM system"), &originalCount) + database.MustScan(t, "counting system configs", db.QueryRow("SELECT count(*) FROM system"), &originalCount) // Execute tx, err := db.Begin() @@ -85,11 +83,11 @@ func TestInitSystemKV_existing(t *testing.T) { // Test var count int - testutils.MustScan(t, "counting system configs", db.QueryRow("SELECT count(*) FROM system"), &count) - testutils.AssertEqual(t, count, originalCount, "system count mismatch") + database.MustScan(t, "counting system configs", db.QueryRow("SELECT count(*) FROM system"), &count) + assert.Equal(t, count, originalCount, "system count mismatch") var val string - testutils.MustScan(t, "getting system value", + database.MustScan(t, "getting system value", db.QueryRow("SELECT value FROM system WHERE key = ?", "testKey"), &val) - testutils.AssertEqual(t, val, "testVal", "system value should not have been updated") + assert.Equal(t, val, "testVal", "system value should not have been updated") } diff --git a/cli/install.sh b/pkg/cli/install.sh similarity index 100% rename from cli/install.sh rename to pkg/cli/install.sh diff --git a/cli/log/log.go b/pkg/cli/log/log.go similarity index 100% rename from cli/log/log.go rename to pkg/cli/log/log.go diff --git a/pkg/cli/main.go b/pkg/cli/main.go new file mode 100644 index 00000000..5552ccba --- /dev/null +++ b/pkg/cli/main.go @@ -0,0 +1,71 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +package main + +import ( + "os" + + "github.com/dnote/dnote/pkg/cli/infra" + "github.com/dnote/dnote/pkg/cli/log" + _ "github.com/mattn/go-sqlite3" + "github.com/pkg/errors" + + // commands + "github.com/dnote/dnote/pkg/cli/cmd/add" + "github.com/dnote/dnote/pkg/cli/cmd/cat" + "github.com/dnote/dnote/pkg/cli/cmd/edit" + "github.com/dnote/dnote/pkg/cli/cmd/find" + "github.com/dnote/dnote/pkg/cli/cmd/login" + "github.com/dnote/dnote/pkg/cli/cmd/logout" + "github.com/dnote/dnote/pkg/cli/cmd/ls" + "github.com/dnote/dnote/pkg/cli/cmd/remove" + "github.com/dnote/dnote/pkg/cli/cmd/root" + "github.com/dnote/dnote/pkg/cli/cmd/sync" + "github.com/dnote/dnote/pkg/cli/cmd/version" + "github.com/dnote/dnote/pkg/cli/cmd/view" +) + +// apiEndpoint and versionTag are populated during link time +var apiEndpoint string +var versionTag = "master" + +func main() { + ctx, err := infra.Init(apiEndpoint, versionTag) + if err != nil { + panic(errors.Wrap(err, "initializing context")) + } + defer ctx.DB.Close() + + root.Register(remove.NewCmd(*ctx)) + root.Register(edit.NewCmd(*ctx)) + root.Register(login.NewCmd(*ctx)) + root.Register(logout.NewCmd(*ctx)) + root.Register(add.NewCmd(*ctx)) + root.Register(ls.NewCmd(*ctx)) + root.Register(sync.NewCmd(*ctx)) + root.Register(version.NewCmd(*ctx)) + root.Register(cat.NewCmd(*ctx)) + root.Register(view.NewCmd(*ctx)) + root.Register(find.NewCmd(*ctx)) + + if err := root.Execute(); err != nil { + log.Errorf("%s\n", err.Error()) + os.Exit(1) + } +} diff --git a/pkg/cli/main_test.go b/pkg/cli/main_test.go new file mode 100644 index 00000000..f7102b3a --- /dev/null +++ b/pkg/cli/main_test.go @@ -0,0 +1,333 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +package main + +import ( + "fmt" + "log" + "os" + "os/exec" + "testing" + + "github.com/dnote/dnote/pkg/assert" + "github.com/dnote/dnote/pkg/cli/consts" + // "github.com/dnote/dnote/pkg/cli/core" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/infra" + "github.com/dnote/dnote/pkg/cli/testutils" + "github.com/dnote/dnote/pkg/cli/utils" + "github.com/pkg/errors" +) + +var binaryName = "test-dnote" + +var opts = testutils.RunDnoteCmdOptions{ + HomeDir: "./tmp", + DnoteDir: "./tmp/.dnote", +} + +func TestMain(m *testing.M) { + if err := exec.Command("go", "build", "--tags", "fts5", "-o", binaryName).Run(); err != nil { + log.Print(errors.Wrap(err, "building a binary").Error()) + os.Exit(1) + } + + os.Exit(m.Run()) +} + +func TestInit(t *testing.T) { + // Execute + testutils.RunDnoteCmd(t, opts, binaryName) + defer testutils.RemoveDir(t, opts.HomeDir) + + db := database.OpenTestDB(t, opts.DnoteDir) + + // Test + if !utils.FileExists(opts.DnoteDir) { + t.Errorf("dnote directory was not initialized") + } + if !utils.FileExists(fmt.Sprintf("%s/%s", opts.DnoteDir, infra.ConfigFilename)) { + t.Errorf("config file was not initialized") + } + + var notesTableCount, booksTableCount, systemTableCount int + database.MustScan(t, "counting notes", + db.QueryRow("SELECT count(*) FROM sqlite_master WHERE type = ? AND name = ?", "table", "notes"), ¬esTableCount) + database.MustScan(t, "counting books", + db.QueryRow("SELECT count(*) FROM sqlite_master WHERE type = ? AND name = ?", "table", "books"), &booksTableCount) + database.MustScan(t, "counting system", + db.QueryRow("SELECT count(*) FROM sqlite_master WHERE type = ? AND name = ?", "table", "system"), &systemTableCount) + + assert.Equal(t, notesTableCount, 1, "notes table count mismatch") + assert.Equal(t, booksTableCount, 1, "books table count mismatch") + assert.Equal(t, systemTableCount, 1, "system table count mismatch") + + // test that all default system configurations are generated + var lastUpgrade, lastMaxUSN, lastSyncAt string + database.MustScan(t, "scanning last upgrade", + db.QueryRow("SELECT value FROM system WHERE key = ?", consts.SystemLastUpgrade), &lastUpgrade) + database.MustScan(t, "scanning last max usn", + db.QueryRow("SELECT value FROM system WHERE key = ?", consts.SystemLastMaxUSN), &lastMaxUSN) + database.MustScan(t, "scanning last sync at", + db.QueryRow("SELECT value FROM system WHERE key = ?", consts.SystemLastSyncAt), &lastSyncAt) + + assert.NotEqual(t, lastUpgrade, "", "last upgrade should not be empty") + assert.NotEqual(t, lastMaxUSN, "", "last max usn should not be empty") + assert.NotEqual(t, lastSyncAt, "", "last sync at should not be empty") +} + +func TestAddNote_NewBook_BodyFlag(t *testing.T) { + // Set up + testutils.RunDnoteCmd(t, opts, binaryName, "add", "js", "-c", "foo") + defer testutils.RemoveDir(t, opts.HomeDir) + + db := database.OpenTestDB(t, opts.DnoteDir) + + // Execute + + // Test + var noteCount, bookCount int + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + + assert.Equalf(t, bookCount, 1, "book count mismatch") + assert.Equalf(t, noteCount, 1, "note count mismatch") + + var book database.Book + database.MustScan(t, "getting book", db.QueryRow("SELECT uuid, dirty FROM books where label = ?", "js"), &book.UUID, &book.Dirty) + var note database.Note + database.MustScan(t, "getting note", + db.QueryRow("SELECT uuid, body, added_on, dirty FROM notes where book_uuid = ?", book.UUID), ¬e.UUID, ¬e.Body, ¬e.AddedOn, ¬e.Dirty) + + assert.Equal(t, book.Dirty, true, "Book dirty mismatch") + + assert.NotEqual(t, note.UUID, "", "Note should have UUID") + assert.Equal(t, note.Body, "foo", "Note body mismatch") + assert.Equal(t, note.Dirty, true, "Note dirty mismatch") + assert.NotEqual(t, note.AddedOn, int64(0), "Note added_on mismatch") +} + +func TestAddNote_ExistingBook_BodyFlag(t *testing.T) { + // Setup + db := database.InitTestDB(t, fmt.Sprintf("%s/%s", opts.DnoteDir, consts.DnoteDBFileName), nil) + testutils.Setup3(t, db) + + // Execute + testutils.RunDnoteCmd(t, opts, binaryName, "add", "js", "-c", "foo") + defer testutils.RemoveDir(t, opts.HomeDir) + + // Test + + var noteCount, bookCount int + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + + assert.Equalf(t, bookCount, 1, "book count mismatch") + assert.Equalf(t, noteCount, 2, "note count mismatch") + + var n1, n2 database.Note + database.MustScan(t, "getting n1", + db.QueryRow("SELECT uuid, body, added_on, dirty FROM notes WHERE book_uuid = ? AND uuid = ?", "js-book-uuid", "43827b9a-c2b0-4c06-a290-97991c896653"), &n1.UUID, &n1.Body, &n1.AddedOn, &n1.Dirty) + database.MustScan(t, "getting n2", + db.QueryRow("SELECT uuid, body, added_on, dirty FROM notes WHERE book_uuid = ? AND body = ?", "js-book-uuid", "foo"), &n2.UUID, &n2.Body, &n2.AddedOn, &n2.Dirty) + + var book database.Book + database.MustScan(t, "getting book", db.QueryRow("SELECT dirty FROM books where label = ?", "js"), &book.Dirty) + + assert.Equal(t, book.Dirty, false, "Book dirty mismatch") + + assert.NotEqual(t, n1.UUID, "", "n1 should have UUID") + assert.Equal(t, n1.Body, "Booleans have toString()", "n1 body mismatch") + assert.Equal(t, n1.AddedOn, int64(1515199943), "n1 added_on mismatch") + assert.Equal(t, n1.Dirty, false, "n1 dirty mismatch") + + assert.NotEqual(t, n2.UUID, "", "n2 should have UUID") + assert.Equal(t, n2.Body, "foo", "n2 body mismatch") + assert.Equal(t, n2.Dirty, true, "n2 dirty mismatch") +} + +func TestEditNote_BodyFlag(t *testing.T) { + // Setup + db := database.InitTestDB(t, fmt.Sprintf("%s/%s", opts.DnoteDir, consts.DnoteDBFileName), nil) + testutils.Setup4(t, db) + + // Execute + testutils.RunDnoteCmd(t, opts, binaryName, "edit", "2", "-c", "foo bar") + defer testutils.RemoveDir(t, opts.HomeDir) + + // Test + + var noteCount, bookCount int + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + + assert.Equalf(t, bookCount, 1, "book count mismatch") + assert.Equalf(t, noteCount, 2, "note count mismatch") + + var n1, n2 database.Note + database.MustScan(t, "getting n1", + db.QueryRow("SELECT uuid, body, added_on, dirty FROM notes where book_uuid = ? AND uuid = ?", "js-book-uuid", "43827b9a-c2b0-4c06-a290-97991c896653"), &n1.UUID, &n1.Body, &n1.AddedOn, &n1.Dirty) + database.MustScan(t, "getting n2", + db.QueryRow("SELECT uuid, body, added_on, dirty FROM notes where book_uuid = ? AND uuid = ?", "js-book-uuid", "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f"), &n2.UUID, &n2.Body, &n2.AddedOn, &n2.Dirty) + + assert.Equal(t, n1.UUID, "43827b9a-c2b0-4c06-a290-97991c896653", "n1 should have UUID") + assert.Equal(t, n1.Body, "Booleans have toString()", "n1 body mismatch") + assert.Equal(t, n1.Dirty, false, "n1 dirty mismatch") + + assert.Equal(t, n2.UUID, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "Note should have UUID") + assert.Equal(t, n2.Body, "foo bar", "Note body mismatch") + assert.Equal(t, n2.Dirty, true, "n2 dirty mismatch") + assert.NotEqual(t, n2.EditedOn, 0, "Note edited_on mismatch") +} + +func TestRemoveNote(t *testing.T) { + // Setup + db := database.InitTestDB(t, fmt.Sprintf("%s/%s", opts.DnoteDir, consts.DnoteDBFileName), nil) + testutils.Setup2(t, db) + + // Execute + testutils.WaitDnoteCmd(t, opts, testutils.UserConfirm, binaryName, "remove", "js", "1") + defer testutils.RemoveDir(t, opts.HomeDir) + + // Test + var noteCount, bookCount, jsNoteCount, linuxNoteCount int + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting js notes", db.QueryRow("SELECT count(*) FROM notes WHERE book_uuid = ?", "js-book-uuid"), &jsNoteCount) + database.MustScan(t, "counting linux notes", db.QueryRow("SELECT count(*) FROM notes WHERE book_uuid = ?", "linux-book-uuid"), &linuxNoteCount) + + assert.Equalf(t, bookCount, 2, "book count mismatch") + assert.Equalf(t, noteCount, 3, "note count mismatch") + assert.Equal(t, jsNoteCount, 2, "js book should have 2 notes") + assert.Equal(t, linuxNoteCount, 1, "linux book book should have 1 note") + + var b1, b2 database.Book + var n1, n2, n3 database.Note + database.MustScan(t, "getting b1", + db.QueryRow("SELECT label, deleted, usn FROM books WHERE uuid = ?", "js-book-uuid"), + &b1.Label, &b1.Deleted, &b1.USN) + database.MustScan(t, "getting b2", + db.QueryRow("SELECT label, deleted, usn FROM books WHERE uuid = ?", "linux-book-uuid"), + &b2.Label, &b2.Deleted, &b2.USN) + database.MustScan(t, "getting n1", + db.QueryRow("SELECT uuid, body, added_on, deleted, dirty, usn FROM notes WHERE book_uuid = ? AND uuid = ?", "js-book-uuid", "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f"), + &n1.UUID, &n1.Body, &n1.AddedOn, &n1.Deleted, &n1.Dirty, &n1.USN) + database.MustScan(t, "getting n2", + db.QueryRow("SELECT uuid, body, added_on, deleted, dirty, usn FROM notes WHERE book_uuid = ? AND uuid = ?", "js-book-uuid", "43827b9a-c2b0-4c06-a290-97991c896653"), + &n2.UUID, &n2.Body, &n2.AddedOn, &n2.Deleted, &n2.Dirty, &n2.USN) + database.MustScan(t, "getting n3", + db.QueryRow("SELECT uuid, body, added_on, deleted, dirty, usn FROM notes WHERE book_uuid = ? AND uuid = ?", "linux-book-uuid", "3e065d55-6d47-42f2-a6bf-f5844130b2d2"), + &n3.UUID, &n3.Body, &n3.AddedOn, &n3.Deleted, &n3.Dirty, &n3.USN) + + assert.Equal(t, b1.Label, "js", "b1 label mismatch") + assert.Equal(t, b1.Deleted, false, "b1 deleted mismatch") + assert.Equal(t, b1.Dirty, false, "b1 Dirty mismatch") + assert.Equal(t, b1.USN, 111, "b1 usn mismatch") + + assert.Equal(t, b2.Label, "linux", "b2 label mismatch") + assert.Equal(t, b2.Deleted, false, "b2 deleted mismatch") + assert.Equal(t, b2.Dirty, false, "b2 Dirty mismatch") + assert.Equal(t, b2.USN, 122, "b2 usn mismatch") + + assert.Equal(t, n1.UUID, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "n1 should have UUID") + assert.Equal(t, n1.Body, "", "n1 body mismatch") + assert.Equal(t, n1.Deleted, true, "n1 deleted mismatch") + assert.Equal(t, n1.Dirty, true, "n1 Dirty mismatch") + assert.Equal(t, n1.USN, 11, "n1 usn mismatch") + + assert.Equal(t, n2.UUID, "43827b9a-c2b0-4c06-a290-97991c896653", "n2 should have UUID") + assert.Equal(t, n2.Body, "n2 body", "n2 body mismatch") + assert.Equal(t, n2.Deleted, false, "n2 deleted mismatch") + assert.Equal(t, n2.Dirty, false, "n2 Dirty mismatch") + assert.Equal(t, n2.USN, 12, "n2 usn mismatch") + + assert.Equal(t, n3.UUID, "3e065d55-6d47-42f2-a6bf-f5844130b2d2", "n3 should have UUID") + assert.Equal(t, n3.Body, "n3 body", "n3 body mismatch") + assert.Equal(t, n3.Deleted, false, "n3 deleted mismatch") + assert.Equal(t, n3.Dirty, false, "n3 Dirty mismatch") + assert.Equal(t, n3.USN, 13, "n3 usn mismatch") +} + +func TestRemoveBook(t *testing.T) { + // Setup + db := database.InitTestDB(t, fmt.Sprintf("%s/%s", opts.DnoteDir, consts.DnoteDBFileName), nil) + testutils.Setup2(t, db) + + // Execute + testutils.WaitDnoteCmd(t, opts, testutils.UserConfirm, binaryName, "remove", "-b", "js") + defer testutils.RemoveDir(t, opts.HomeDir) + + // Test + var noteCount, bookCount, jsNoteCount, linuxNoteCount int + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + database.MustScan(t, "counting notes", db.QueryRow("SELECT count(*) FROM notes"), ¬eCount) + database.MustScan(t, "counting js notes", db.QueryRow("SELECT count(*) FROM notes WHERE book_uuid = ?", "js-book-uuid"), &jsNoteCount) + database.MustScan(t, "counting linux notes", db.QueryRow("SELECT count(*) FROM notes WHERE book_uuid = ?", "linux-book-uuid"), &linuxNoteCount) + + assert.Equalf(t, bookCount, 2, "book count mismatch") + assert.Equalf(t, noteCount, 3, "note count mismatch") + assert.Equal(t, jsNoteCount, 2, "js book should have 2 notes") + assert.Equal(t, linuxNoteCount, 1, "linux book book should have 1 note") + + var b1, b2 database.Book + var n1, n2, n3 database.Note + database.MustScan(t, "getting b1", + db.QueryRow("SELECT label, dirty, deleted, usn FROM books WHERE uuid = ?", "js-book-uuid"), + &b1.Label, &b1.Dirty, &b1.Deleted, &b1.USN) + database.MustScan(t, "getting b2", + db.QueryRow("SELECT label, dirty, deleted, usn FROM books WHERE uuid = ?", "linux-book-uuid"), + &b2.Label, &b2.Dirty, &b2.Deleted, &b2.USN) + database.MustScan(t, "getting n1", + db.QueryRow("SELECT uuid, body, added_on, dirty, deleted, usn FROM notes WHERE book_uuid = ? AND uuid = ?", "js-book-uuid", "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f"), + &n1.UUID, &n1.Body, &n1.AddedOn, &n1.Deleted, &n1.Dirty, &n1.USN) + database.MustScan(t, "getting n2", + db.QueryRow("SELECT uuid, body, added_on, dirty, deleted, usn FROM notes WHERE book_uuid = ? AND uuid = ?", "js-book-uuid", "43827b9a-c2b0-4c06-a290-97991c896653"), + &n2.UUID, &n2.Body, &n2.AddedOn, &n2.Deleted, &n2.Dirty, &n2.USN) + database.MustScan(t, "getting n3", + db.QueryRow("SELECT uuid, body, added_on, dirty, deleted, usn FROM notes WHERE book_uuid = ? AND uuid = ?", "linux-book-uuid", "3e065d55-6d47-42f2-a6bf-f5844130b2d2"), + &n3.UUID, &n3.Body, &n3.AddedOn, &n3.Deleted, &n3.Dirty, &n3.USN) + + assert.NotEqual(t, b1.Label, "js", "b1 label mismatch") + assert.Equal(t, b1.Dirty, true, "b1 Dirty mismatch") + assert.Equal(t, b1.Deleted, true, "b1 deleted mismatch") + assert.Equal(t, b1.USN, 111, "b1 usn mismatch") + + assert.Equal(t, b2.Label, "linux", "b2 label mismatch") + assert.Equal(t, b2.Dirty, false, "b2 Dirty mismatch") + assert.Equal(t, b2.Deleted, false, "b2 deleted mismatch") + assert.Equal(t, b2.USN, 122, "b2 usn mismatch") + + assert.Equal(t, n1.UUID, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", "n1 should have UUID") + assert.Equal(t, n1.Body, "", "n1 body mismatch") + assert.Equal(t, n1.Dirty, true, "n1 Dirty mismatch") + assert.Equal(t, n1.Deleted, true, "n1 deleted mismatch") + assert.Equal(t, n1.USN, 11, "n1 usn mismatch") + + assert.Equal(t, n2.UUID, "43827b9a-c2b0-4c06-a290-97991c896653", "n2 should have UUID") + assert.Equal(t, n2.Body, "", "n2 body mismatch") + assert.Equal(t, n2.Dirty, true, "n2 Dirty mismatch") + assert.Equal(t, n2.Deleted, true, "n2 deleted mismatch") + assert.Equal(t, n2.USN, 12, "n2 usn mismatch") + + assert.Equal(t, n3.UUID, "3e065d55-6d47-42f2-a6bf-f5844130b2d2", "n3 should have UUID") + assert.Equal(t, n3.Body, "n3 body", "n3 body mismatch") + assert.Equal(t, n3.Dirty, false, "n3 Dirty mismatch") + assert.Equal(t, n3.Deleted, false, "n3 deleted mismatch") + assert.Equal(t, n3.USN, 13, "n3 usn mismatch") +} diff --git a/cli/migrate/fixtures/legacy-2-pre-dnote.json b/pkg/cli/migrate/fixtures/legacy-2-pre-dnote.json similarity index 100% rename from cli/migrate/fixtures/legacy-2-pre-dnote.json rename to pkg/cli/migrate/fixtures/legacy-2-pre-dnote.json diff --git a/cli/migrate/fixtures/legacy-3-pre-dnote.json b/pkg/cli/migrate/fixtures/legacy-3-pre-dnote.json similarity index 100% rename from cli/migrate/fixtures/legacy-3-pre-dnote.json rename to pkg/cli/migrate/fixtures/legacy-3-pre-dnote.json diff --git a/cli/migrate/fixtures/legacy-4-pre-dnoterc.yaml b/pkg/cli/migrate/fixtures/legacy-4-pre-dnoterc.yaml similarity index 100% rename from cli/migrate/fixtures/legacy-4-pre-dnoterc.yaml rename to pkg/cli/migrate/fixtures/legacy-4-pre-dnoterc.yaml diff --git a/cli/migrate/fixtures/legacy-5-post-actions.json b/pkg/cli/migrate/fixtures/legacy-5-post-actions.json similarity index 100% rename from cli/migrate/fixtures/legacy-5-post-actions.json rename to pkg/cli/migrate/fixtures/legacy-5-post-actions.json diff --git a/cli/migrate/fixtures/legacy-5-pre-actions.json b/pkg/cli/migrate/fixtures/legacy-5-pre-actions.json similarity index 100% rename from cli/migrate/fixtures/legacy-5-pre-actions.json rename to pkg/cli/migrate/fixtures/legacy-5-pre-actions.json diff --git a/cli/migrate/fixtures/legacy-6-post-dnote.json b/pkg/cli/migrate/fixtures/legacy-6-post-dnote.json similarity index 100% rename from cli/migrate/fixtures/legacy-6-post-dnote.json rename to pkg/cli/migrate/fixtures/legacy-6-post-dnote.json diff --git a/cli/migrate/fixtures/legacy-6-pre-dnote.json b/pkg/cli/migrate/fixtures/legacy-6-pre-dnote.json similarity index 100% rename from cli/migrate/fixtures/legacy-6-pre-dnote.json rename to pkg/cli/migrate/fixtures/legacy-6-pre-dnote.json diff --git a/cli/migrate/fixtures/legacy-7-post-actions.json b/pkg/cli/migrate/fixtures/legacy-7-post-actions.json similarity index 100% rename from cli/migrate/fixtures/legacy-7-post-actions.json rename to pkg/cli/migrate/fixtures/legacy-7-post-actions.json diff --git a/cli/migrate/fixtures/legacy-7-pre-actions.json b/pkg/cli/migrate/fixtures/legacy-7-pre-actions.json similarity index 100% rename from cli/migrate/fixtures/legacy-7-pre-actions.json rename to pkg/cli/migrate/fixtures/legacy-7-pre-actions.json diff --git a/cli/migrate/fixtures/legacy-8-actions.json b/pkg/cli/migrate/fixtures/legacy-8-actions.json similarity index 100% rename from cli/migrate/fixtures/legacy-8-actions.json rename to pkg/cli/migrate/fixtures/legacy-8-actions.json diff --git a/cli/migrate/fixtures/legacy-8-dnote.json b/pkg/cli/migrate/fixtures/legacy-8-dnote.json similarity index 100% rename from cli/migrate/fixtures/legacy-8-dnote.json rename to pkg/cli/migrate/fixtures/legacy-8-dnote.json diff --git a/cli/migrate/fixtures/legacy-8-dnoterc.yaml b/pkg/cli/migrate/fixtures/legacy-8-dnoterc.yaml similarity index 100% rename from cli/migrate/fixtures/legacy-8-dnoterc.yaml rename to pkg/cli/migrate/fixtures/legacy-8-dnoterc.yaml diff --git a/cli/migrate/fixtures/legacy-8-schema.yaml b/pkg/cli/migrate/fixtures/legacy-8-schema.yaml similarity index 100% rename from cli/migrate/fixtures/legacy-8-schema.yaml rename to pkg/cli/migrate/fixtures/legacy-8-schema.yaml diff --git a/cli/migrate/fixtures/legacy-8-timestamps.yaml b/pkg/cli/migrate/fixtures/legacy-8-timestamps.yaml similarity index 100% rename from cli/migrate/fixtures/legacy-8-timestamps.yaml rename to pkg/cli/migrate/fixtures/legacy-8-timestamps.yaml diff --git a/cli/migrate/fixtures/local-1-pre-schema.sql b/pkg/cli/migrate/fixtures/local-1-pre-schema.sql similarity index 100% rename from cli/migrate/fixtures/local-1-pre-schema.sql rename to pkg/cli/migrate/fixtures/local-1-pre-schema.sql diff --git a/cli/migrate/fixtures/local-10-pre-schema.sql b/pkg/cli/migrate/fixtures/local-10-pre-schema.sql similarity index 100% rename from cli/migrate/fixtures/local-10-pre-schema.sql rename to pkg/cli/migrate/fixtures/local-10-pre-schema.sql diff --git a/cli/migrate/fixtures/local-11-pre-schema.sql b/pkg/cli/migrate/fixtures/local-11-pre-schema.sql similarity index 100% rename from cli/migrate/fixtures/local-11-pre-schema.sql rename to pkg/cli/migrate/fixtures/local-11-pre-schema.sql diff --git a/cli/migrate/fixtures/local-5-pre-schema.sql b/pkg/cli/migrate/fixtures/local-5-pre-schema.sql similarity index 100% rename from cli/migrate/fixtures/local-5-pre-schema.sql rename to pkg/cli/migrate/fixtures/local-5-pre-schema.sql diff --git a/cli/migrate/fixtures/local-7-pre-schema.sql b/pkg/cli/migrate/fixtures/local-7-pre-schema.sql similarity index 100% rename from cli/migrate/fixtures/local-7-pre-schema.sql rename to pkg/cli/migrate/fixtures/local-7-pre-schema.sql diff --git a/cli/migrate/fixtures/local-8-pre-schema.sql b/pkg/cli/migrate/fixtures/local-8-pre-schema.sql similarity index 100% rename from cli/migrate/fixtures/local-8-pre-schema.sql rename to pkg/cli/migrate/fixtures/local-8-pre-schema.sql diff --git a/cli/migrate/fixtures/local-9-pre-schema.sql b/pkg/cli/migrate/fixtures/local-9-pre-schema.sql similarity index 100% rename from cli/migrate/fixtures/local-9-pre-schema.sql rename to pkg/cli/migrate/fixtures/local-9-pre-schema.sql diff --git a/cli/migrate/fixtures/remote-1-pre-schema.sql b/pkg/cli/migrate/fixtures/remote-1-pre-schema.sql similarity index 100% rename from cli/migrate/fixtures/remote-1-pre-schema.sql rename to pkg/cli/migrate/fixtures/remote-1-pre-schema.sql diff --git a/cli/migrate/legacy.go b/pkg/cli/migrate/legacy.go similarity index 95% rename from cli/migrate/legacy.go rename to pkg/cli/migrate/legacy.go index ee79a07b..ea537caa 100644 --- a/cli/migrate/legacy.go +++ b/pkg/cli/migrate/legacy.go @@ -27,9 +27,9 @@ import ( "os" "time" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" - "github.com/dnote/dnote/cli/utils" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/log" + "github.com/dnote/dnote/pkg/cli/utils" "github.com/pkg/errors" "github.com/satori/go.uuid" "gopkg.in/yaml.v2" @@ -82,7 +82,7 @@ func makeSchema(complete bool) schema { } // Legacy performs migration on JSON-based dnote if necessary -func Legacy(ctx infra.DnoteCtx) error { +func Legacy(ctx context.DnoteCtx) error { // If schema does not exist, no need run a legacy migration schemaPath := getSchemaPath(ctx) if ok := utils.FileExists(schemaPath); !ok { @@ -106,7 +106,7 @@ func Legacy(ctx infra.DnoteCtx) error { // performMigration backs up current .dnote data, performs migration, and // restores or cleans backups depending on if there is an error -func performMigration(ctx infra.DnoteCtx, migrationID int) error { +func performMigration(ctx context.DnoteCtx, migrationID int) error { // legacyMigrationV8 is the final migration of the legacy JSON Dnote migration // migrate to sqlite and return if migrationID == legacyMigrationV8 { @@ -162,7 +162,7 @@ func performMigration(ctx infra.DnoteCtx, migrationID int) error { } // backupDnoteDir backs up the dnote directory to a temporary backup directory -func backupDnoteDir(ctx infra.DnoteCtx) error { +func backupDnoteDir(ctx context.DnoteCtx) error { srcPath := fmt.Sprintf("%s/.dnote", ctx.HomeDir) tmpPath := fmt.Sprintf("%s/%s", ctx.HomeDir, backupDirName) @@ -173,14 +173,14 @@ func backupDnoteDir(ctx infra.DnoteCtx) error { return nil } -func restoreBackup(ctx infra.DnoteCtx) error { +func restoreBackup(ctx context.DnoteCtx) error { var err error defer func() { if err != nil { log.Printf(`Failed to restore backup for a failed migration. Don't worry. Your data is still intact in the backup directory. - Get help on https://github.com/dnote/dnote/cli/issues`) + Get help on https://github.com/dnote/dnote/pkg/cli/issues`) } }() @@ -198,7 +198,7 @@ func restoreBackup(ctx infra.DnoteCtx) error { return nil } -func clearBackup(ctx infra.DnoteCtx) error { +func clearBackup(ctx context.DnoteCtx) error { backupPath := fmt.Sprintf("%s/%s", ctx.HomeDir, backupDirName) if err := os.RemoveAll(backupPath); err != nil { @@ -209,11 +209,11 @@ func clearBackup(ctx infra.DnoteCtx) error { } // getSchemaPath returns the path to the file containing schema info -func getSchemaPath(ctx infra.DnoteCtx) string { +func getSchemaPath(ctx context.DnoteCtx) string { return fmt.Sprintf("%s/%s", ctx.DnoteDir, schemaFilename) } -func readSchema(ctx infra.DnoteCtx) (schema, error) { +func readSchema(ctx context.DnoteCtx) (schema, error) { var ret schema path := getSchemaPath(ctx) @@ -231,7 +231,7 @@ func readSchema(ctx infra.DnoteCtx) (schema, error) { return ret, nil } -func writeSchema(ctx infra.DnoteCtx, s schema) error { +func writeSchema(ctx context.DnoteCtx, s schema) error { path := getSchemaPath(ctx) d, err := yaml.Marshal(&s) if err != nil { @@ -245,7 +245,7 @@ func writeSchema(ctx infra.DnoteCtx, s schema) error { return nil } -func getUnrunMigrations(ctx infra.DnoteCtx) ([]int, error) { +func getUnrunMigrations(ctx context.DnoteCtx) ([]int, error) { var ret []int schema, err := readSchema(ctx) @@ -265,7 +265,7 @@ func getUnrunMigrations(ctx infra.DnoteCtx) ([]int, error) { return ret, nil } -func updateSchemaVersion(ctx infra.DnoteCtx, mID int) error { +func updateSchemaVersion(ctx context.DnoteCtx, mID int) error { s, err := readSchema(ctx) if err != nil { return errors.Wrap(err, "Failed to read schema") @@ -470,7 +470,7 @@ var migrateToV8SystemKeyBookMark = "bookmark" /***** migrations **/ // migrateToV1 deletes YAML archive if exists -func migrateToV1(ctx infra.DnoteCtx) error { +func migrateToV1(ctx context.DnoteCtx) error { yamlPath := fmt.Sprintf("%s/%s", ctx.HomeDir, ".dnote-yaml-archived") if !utils.FileExists(yamlPath) { return nil @@ -483,7 +483,7 @@ func migrateToV1(ctx infra.DnoteCtx) error { return nil } -func migrateToV2(ctx infra.DnoteCtx) error { +func migrateToV2(ctx context.DnoteCtx) error { notePath := fmt.Sprintf("%s/dnote", ctx.DnoteDir) b, err := ioutil.ReadFile(notePath) @@ -534,7 +534,7 @@ func migrateToV2(ctx infra.DnoteCtx) error { } // migrateToV3 generates actions for existing dnote -func migrateToV3(ctx infra.DnoteCtx) error { +func migrateToV3(ctx context.DnoteCtx) error { notePath := fmt.Sprintf("%s/dnote", ctx.DnoteDir) actionsPath := fmt.Sprintf("%s/actions", ctx.DnoteDir) @@ -621,7 +621,7 @@ func getEditorCommand() string { } } -func migrateToV4(ctx infra.DnoteCtx) error { +func migrateToV4(ctx context.DnoteCtx) error { configPath := fmt.Sprintf("%s/dnoterc", ctx.DnoteDir) b, err := ioutil.ReadFile(configPath) @@ -654,7 +654,7 @@ func migrateToV4(ctx infra.DnoteCtx) error { } // migrateToV5 migrates actions -func migrateToV5(ctx infra.DnoteCtx) error { +func migrateToV5(ctx context.DnoteCtx) error { actionsPath := fmt.Sprintf("%s/actions", ctx.DnoteDir) b, err := ioutil.ReadFile(actionsPath) @@ -719,7 +719,7 @@ func migrateToV5(ctx infra.DnoteCtx) error { } // migrateToV6 adds a 'public' field to notes -func migrateToV6(ctx infra.DnoteCtx) error { +func migrateToV6(ctx context.DnoteCtx) error { notePath := fmt.Sprintf("%s/dnote", ctx.DnoteDir) b, err := ioutil.ReadFile(notePath) @@ -773,8 +773,8 @@ func migrateToV6(ctx infra.DnoteCtx) error { // migrateToV7 migrates data of edit_note action to the proper version which is // EditNoteDataV2. Due to a bug, edit logged actions with schema version '2' -// but with a data of EditNoteDataV1. https://github.com/dnote/dnote/cli/issues/107 -func migrateToV7(ctx infra.DnoteCtx) error { +// but with a data of EditNoteDataV1. https://github.com/dnote/dnote/pkg/cli/issues/107 +func migrateToV7(ctx context.DnoteCtx) error { actionPath := fmt.Sprintf("%s/actions", ctx.DnoteDir) b, err := ioutil.ReadFile(actionPath) @@ -838,7 +838,7 @@ func migrateToV7(ctx infra.DnoteCtx) error { } // migrateToV8 migrates dnote data to sqlite database -func migrateToV8(ctx infra.DnoteCtx) error { +func migrateToV8(ctx context.DnoteCtx) error { tx, err := ctx.DB.Begin() if err != nil { return errors.Wrap(err, "beginning a transaction") diff --git a/cli/migrate/legacy_test.go b/pkg/cli/migrate/legacy_test.go similarity index 53% rename from cli/migrate/legacy_test.go rename to pkg/cli/migrate/legacy_test.go index 13100036..012af095 100644 --- a/cli/migrate/legacy_test.go +++ b/pkg/cli/migrate/legacy_test.go @@ -27,18 +27,38 @@ import ( "reflect" "testing" - "github.com/dnote/dnote/cli/testutils" - "github.com/dnote/dnote/cli/utils" + "github.com/dnote/dnote/pkg/assert" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/testutils" + "github.com/dnote/dnote/pkg/cli/utils" "github.com/pkg/errors" "gopkg.in/yaml.v2" ) -func TestMigrateToV1(t *testing.T) { +func setupEnv(t *testing.T, homeDir string) context.DnoteCtx { + dnoteDir := fmt.Sprintf("%s/.dnote", homeDir) + if err := os.MkdirAll(dnoteDir, 0755); err != nil { + t.Fatal(errors.Wrap(err, "preparing dnote dir")) + } + return context.DnoteCtx{ + HomeDir: homeDir, + DnoteDir: dnoteDir, + } +} + +func teardownEnv(t *testing.T, ctx context.DnoteCtx) { + if err := os.RemoveAll(ctx.DnoteDir); err != nil { + t.Fatal(errors.Wrap(err, "tearing down the dnote dir")) + } +} + +func TestMigrateToV1(t *testing.T) { t.Run("yaml exists", func(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + ctx := setupEnv(t, "../tmp") + defer teardownEnv(t, ctx) yamlPath, err := filepath.Abs(filepath.Join(ctx.HomeDir, ".dnote-yaml-archived")) if err != nil { @@ -59,8 +79,8 @@ func TestMigrateToV1(t *testing.T) { t.Run("yaml does not exist", func(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + ctx := setupEnv(t, "../tmp") + defer teardownEnv(t, ctx) yamlPath, err := filepath.Abs(filepath.Join(ctx.HomeDir, ".dnote-yaml-archived")) if err != nil { @@ -80,10 +100,10 @@ func TestMigrateToV1(t *testing.T) { } func TestMigrateToV2(t *testing.T) { - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + ctx := setupEnv(t, "../tmp") + defer teardownEnv(t, ctx) - testutils.CopyFixture(ctx, "./fixtures/legacy-2-pre-dnote.json", "dnote") + testutils.CopyFixture(t, ctx, "./fixtures/legacy-2-pre-dnote.json", "dnote") // execute if err := migrateToV2(ctx); err != nil { @@ -99,25 +119,25 @@ func TestMigrateToV2(t *testing.T) { } for _, book := range postDnote { - testutils.AssertNotEqual(t, book.Name, "", "Book name was not populated") + assert.NotEqual(t, book.Name, "", "Book name was not populated") for _, note := range book.Notes { if len(note.UUID) == 8 { t.Errorf("Note UUID was not migrated. It has length of %d", len(note.UUID)) } - testutils.AssertNotEqual(t, note.AddedOn, int64(0), "AddedOn was not carried over") - testutils.AssertEqual(t, note.EditedOn, int64(0), "EditedOn was not created properly") + assert.NotEqual(t, note.AddedOn, int64(0), "AddedOn was not carried over") + assert.Equal(t, note.EditedOn, int64(0), "EditedOn was not created properly") } } } func TestMigrateToV3(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + ctx := setupEnv(t, "../tmp") + defer teardownEnv(t, ctx) - testutils.CopyFixture(ctx, "./fixtures/legacy-3-pre-dnote.json", "dnote") + testutils.CopyFixture(t, ctx, "./fixtures/legacy-3-pre-dnote.json", "dnote") // execute if err := migrateToV3(ctx); err != nil { @@ -137,22 +157,22 @@ func TestMigrateToV3(t *testing.T) { t.Fatal(errors.Wrap(err, "Failed to unmarshal the actions").Error()) } - testutils.AssertEqual(t, len(actions), 6, "actions length mismatch") + assert.Equal(t, len(actions), 6, "actions length mismatch") for _, book := range postDnote { for _, note := range book.Notes { - testutils.AssertNotEqual(t, note.AddedOn, int64(0), "AddedOn was not carried over") + assert.NotEqual(t, note.AddedOn, int64(0), "AddedOn was not carried over") } } } func TestMigrateToV4(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + ctx := setupEnv(t, "../tmp") + defer teardownEnv(t, ctx) defer os.Setenv("EDITOR", "") - testutils.CopyFixture(ctx, "./fixtures/legacy-4-pre-dnoterc.yaml", "dnoterc") + testutils.CopyFixture(t, ctx, "./fixtures/legacy-4-pre-dnoterc.yaml", "dnoterc") // execute os.Setenv("EDITOR", "vim") @@ -167,16 +187,16 @@ func TestMigrateToV4(t *testing.T) { t.Fatal(errors.Wrap(err, "Failed to unmarshal the result into Dnote").Error()) } - testutils.AssertEqual(t, config.APIKey, "Oev6e1082ORasdf9rjkfjkasdfjhgei", "api key mismatch") - testutils.AssertEqual(t, config.Editor, "vim", "editor mismatch") + assert.Equal(t, config.APIKey, "Oev6e1082ORasdf9rjkfjkasdfjhgei", "api key mismatch") + assert.Equal(t, config.Editor, "vim", "editor mismatch") } func TestMigrateToV5(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + ctx := setupEnv(t, "../tmp") + defer teardownEnv(t, ctx) - testutils.CopyFixture(ctx, "./fixtures/legacy-5-pre-actions.json", "actions") + testutils.CopyFixture(t, ctx, "./fixtures/legacy-5-pre-actions.json", "actions") // execute if err := migrateToV5(ctx); err != nil { @@ -201,10 +221,10 @@ func TestMigrateToV5(t *testing.T) { migrated := migratedActions[idx] old := oldActions[idx] - testutils.AssertNotEqual(t, migrated.UUID, "", fmt.Sprintf("uuid mismatch for migrated item with index %d", idx)) - testutils.AssertEqual(t, migrated.Schema, 1, fmt.Sprintf("schema mismatch for migrated item with index %d", idx)) - testutils.AssertEqual(t, migrated.Timestamp, old.Timestamp, fmt.Sprintf("timestamp mismatch for migrated item with index %d", idx)) - testutils.AssertEqual(t, migrated.Type, old.Type, fmt.Sprintf("timestamp mismatch for migrated item with index %d", idx)) + assert.NotEqual(t, migrated.UUID, "", fmt.Sprintf("uuid mismatch for migrated item with index %d", idx)) + assert.Equal(t, migrated.Schema, 1, fmt.Sprintf("schema mismatch for migrated item with index %d", idx)) + assert.Equal(t, migrated.Timestamp, old.Timestamp, fmt.Sprintf("timestamp mismatch for migrated item with index %d", idx)) + assert.Equal(t, migrated.Type, old.Type, fmt.Sprintf("timestamp mismatch for migrated item with index %d", idx)) switch migrated.Type { case migrateToV5ActionAddNote: @@ -216,9 +236,9 @@ func TestMigrateToV5(t *testing.T) { t.Fatal(errors.Wrap(err, "unmarshalling new data").Error()) } - testutils.AssertEqual(t, oldData.BookName, migratedData.BookName, fmt.Sprintf("data book_name mismatch for item idx %d", idx)) - testutils.AssertEqual(t, oldData.Content, migratedData.Content, fmt.Sprintf("data content mismatch for item idx %d", idx)) - testutils.AssertEqual(t, oldData.NoteUUID, migratedData.NoteUUID, fmt.Sprintf("data note_uuid mismatch for item idx %d", idx)) + assert.Equal(t, oldData.BookName, migratedData.BookName, fmt.Sprintf("data book_name mismatch for item idx %d", idx)) + assert.Equal(t, oldData.Content, migratedData.Content, fmt.Sprintf("data content mismatch for item idx %d", idx)) + assert.Equal(t, oldData.NoteUUID, migratedData.NoteUUID, fmt.Sprintf("data note_uuid mismatch for item idx %d", idx)) case migrateToV5ActionRemoveNote: var oldData, migratedData migrateToV5RemoveNoteData if err := json.Unmarshal(old.Data, &oldData); err != nil { @@ -228,8 +248,8 @@ func TestMigrateToV5(t *testing.T) { t.Fatal(errors.Wrap(err, "unmarshalling new data").Error()) } - testutils.AssertEqual(t, oldData.BookName, migratedData.BookName, fmt.Sprintf("data book_name mismatch for item idx %d", idx)) - testutils.AssertEqual(t, oldData.NoteUUID, migratedData.NoteUUID, fmt.Sprintf("data note_uuid mismatch for item idx %d", idx)) + assert.Equal(t, oldData.BookName, migratedData.BookName, fmt.Sprintf("data book_name mismatch for item idx %d", idx)) + assert.Equal(t, oldData.NoteUUID, migratedData.NoteUUID, fmt.Sprintf("data note_uuid mismatch for item idx %d", idx)) case migrateToV5ActionAddBook: var oldData, migratedData migrateToV5AddBookData if err := json.Unmarshal(old.Data, &oldData); err != nil { @@ -239,7 +259,7 @@ func TestMigrateToV5(t *testing.T) { t.Fatal(errors.Wrap(err, "unmarshalling new data").Error()) } - testutils.AssertEqual(t, oldData.BookName, migratedData.BookName, fmt.Sprintf("data book_name mismatch for item idx %d", idx)) + assert.Equal(t, oldData.BookName, migratedData.BookName, fmt.Sprintf("data book_name mismatch for item idx %d", idx)) case migrateToV5ActionRemoveBook: var oldData, migratedData migrateToV5RemoveBookData if err := json.Unmarshal(old.Data, &oldData); err != nil { @@ -249,7 +269,7 @@ func TestMigrateToV5(t *testing.T) { t.Fatal(errors.Wrap(err, "unmarshalling new data").Error()) } - testutils.AssertEqual(t, oldData.BookName, migratedData.BookName, fmt.Sprintf("data book_name mismatch for item idx %d", idx)) + assert.Equal(t, oldData.BookName, migratedData.BookName, fmt.Sprintf("data book_name mismatch for item idx %d", idx)) case migrateToV5ActionEditNote: var oldData migrateToV5PreEditNoteData var migratedData migrateToV5PostEditNoteData @@ -260,20 +280,20 @@ func TestMigrateToV5(t *testing.T) { t.Fatal(errors.Wrap(err, "unmarshalling new data").Error()) } - testutils.AssertEqual(t, oldData.NoteUUID, migratedData.NoteUUID, fmt.Sprintf("data note_uuid mismatch for item idx %d", idx)) - testutils.AssertEqual(t, oldData.Content, migratedData.Content, fmt.Sprintf("data content mismatch for item idx %d", idx)) - testutils.AssertEqual(t, oldData.BookName, migratedData.FromBook, "book_name should have been renamed to from_book") - testutils.AssertEqual(t, migratedData.ToBook, "", "to_book should be empty") + assert.Equal(t, oldData.NoteUUID, migratedData.NoteUUID, fmt.Sprintf("data note_uuid mismatch for item idx %d", idx)) + assert.Equal(t, oldData.Content, migratedData.Content, fmt.Sprintf("data content mismatch for item idx %d", idx)) + assert.Equal(t, oldData.BookName, migratedData.FromBook, "book_name should have been renamed to from_book") + assert.Equal(t, migratedData.ToBook, "", "to_book should be empty") } } } func TestMigrateToV6(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + ctx := setupEnv(t, "../tmp") + defer teardownEnv(t, ctx) - testutils.CopyFixture(ctx, "./fixtures/legacy-6-pre-dnote.json", "dnote") + testutils.CopyFixture(t, ctx, "./fixtures/legacy-6-pre-dnote.json", "dnote") // execute if err := migrateToV6(ctx); err != nil { @@ -287,7 +307,7 @@ func TestMigrateToV6(t *testing.T) { t.Fatal(errors.Wrap(err, "Failed to unmarshal the result into Dnote").Error()) } - b = testutils.ReadFileAbs("./fixtures/legacy-6-post-dnote.json") + b = utils.ReadFileAbs("./fixtures/legacy-6-post-dnote.json") var expected migrateToV6PostDnote if err := json.Unmarshal(b, &expected); err != nil { t.Fatal(errors.Wrap(err, "Failed to unmarshal the result into Dnote").Error()) @@ -300,10 +320,10 @@ func TestMigrateToV6(t *testing.T) { func TestMigrateToV7(t *testing.T) { // set up - ctx := testutils.InitEnv(t, "../tmp", "../testutils/fixtures/schema.sql", true) - defer testutils.TeardownEnv(ctx) + ctx := setupEnv(t, "../tmp") + defer teardownEnv(t, ctx) - testutils.CopyFixture(ctx, "./fixtures/legacy-7-pre-actions.json", "actions") + testutils.CopyFixture(t, ctx, "./fixtures/legacy-7-pre-actions.json", "actions") // execute if err := migrateToV7(ctx); err != nil { @@ -317,32 +337,28 @@ func TestMigrateToV7(t *testing.T) { t.Fatal(errors.Wrap(err, "unmarshalling the result").Error()) } - b2 := testutils.ReadFileAbs("./fixtures/legacy-7-post-actions.json") + b2 := utils.ReadFileAbs("./fixtures/legacy-7-post-actions.json") var expected []migrateToV7Action if err := json.Unmarshal(b, &expected); err != nil { t.Fatal(errors.Wrap(err, "unmarshalling the result into Dnote").Error()) } - ok, err := testutils.IsEqualJSON(b, b2) - if err != nil { - t.Fatal(errors.Wrap(err, "comparing JSON").Error()) - } - - if !ok { - t.Errorf("Result does not match.\nActual: %+v\nExpected: %+v", got, expected) - } + assert.EqualJSON(t, string(b), string(b2), "Result does not match") } func TestMigrateToV8(t *testing.T) { - ctx := testutils.InitEnv(t, "../tmp", "./fixtures/local-1-pre-schema.sql", false) - defer testutils.TeardownEnv(ctx) + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-1-pre-schema.sql", SkipMigration: true} + db := database.InitTestDB(t, "../tmp/.dnote/dnote-test.db", &opts) + defer database.CloseTestDB(t, db) + + ctx := context.DnoteCtx{HomeDir: "../tmp", DnoteDir: "../tmp/.dnote", DB: db} // set up - testutils.CopyFixture(ctx, "./fixtures/legacy-8-actions.json", "actions") - testutils.CopyFixture(ctx, "./fixtures/legacy-8-dnote.json", "dnote") - testutils.CopyFixture(ctx, "./fixtures/legacy-8-dnoterc.yaml", "dnoterc") - testutils.CopyFixture(ctx, "./fixtures/legacy-8-schema.yaml", "schema") - testutils.CopyFixture(ctx, "./fixtures/legacy-8-timestamps.yaml", "timestamps") + testutils.CopyFixture(t, ctx, "./fixtures/legacy-8-actions.json", "actions") + testutils.CopyFixture(t, ctx, "./fixtures/legacy-8-dnote.json", "dnote") + testutils.CopyFixture(t, ctx, "./fixtures/legacy-8-dnoterc.yaml", "dnoterc") + testutils.CopyFixture(t, ctx, "./fixtures/legacy-8-schema.yaml", "schema") + testutils.CopyFixture(t, ctx, "./fixtures/legacy-8-timestamps.yaml", "timestamps") // execute if err := migrateToV8(ctx); err != nil { @@ -370,7 +386,6 @@ func TestMigrateToV8(t *testing.T) { } // 2. test if notes and books are migrated - db := ctx.DB var bookCount, noteCount int err := db.QueryRow("SELECT count(*) FROM books").Scan(&bookCount) @@ -381,8 +396,8 @@ func TestMigrateToV8(t *testing.T) { if err != nil { panic(errors.Wrap(err, "counting notes")) } - testutils.AssertEqual(t, bookCount, 2, "book count mismatch") - testutils.AssertEqual(t, noteCount, 3, "note count mismatch") + assert.Equal(t, bookCount, 2, "book count mismatch") + assert.Equal(t, noteCount, 3, "note count mismatch") type bookInfo struct { label string @@ -421,34 +436,34 @@ func TestMigrateToV8(t *testing.T) { panic(errors.Wrap(err, "finding note 3")) } - testutils.AssertNotEqual(t, b1.uuid, "", "book 1 uuid should have been generated") - testutils.AssertEqual(t, b1.label, "js", "book 1 label mismatch") - testutils.AssertNotEqual(t, b2.uuid, "", "book 2 uuid should have been generated") - testutils.AssertEqual(t, b2.label, "css", "book 2 label mismatch") + assert.NotEqual(t, b1.uuid, "", "book 1 uuid should have been generated") + assert.Equal(t, b1.label, "js", "book 1 label mismatch") + assert.NotEqual(t, b2.uuid, "", "book 2 uuid should have been generated") + assert.Equal(t, b2.label, "css", "book 2 label mismatch") - testutils.AssertEqual(t, n1.uuid, "d69edb54-5b31-4cdd-a4a5-34f0a0bfa153", "note 1 uuid mismatch") - testutils.AssertNotEqual(t, n1.id, 0, "note 1 id should have been generated") - testutils.AssertEqual(t, n1.bookUUID, b2.uuid, "note 1 book_uuid mismatch") - testutils.AssertEqual(t, n1.content, "css test 1", "note 1 content mismatch") - testutils.AssertEqual(t, n1.addedOn, int64(1536977237), "note 1 added_on mismatch") - testutils.AssertEqual(t, n1.editedOn, int64(1536977253), "note 1 edited_on mismatch") - testutils.AssertEqual(t, n1.public, false, "note 1 public mismatch") + assert.Equal(t, n1.uuid, "d69edb54-5b31-4cdd-a4a5-34f0a0bfa153", "note 1 uuid mismatch") + assert.NotEqual(t, n1.id, 0, "note 1 id should have been generated") + assert.Equal(t, n1.bookUUID, b2.uuid, "note 1 book_uuid mismatch") + assert.Equal(t, n1.content, "css test 1", "note 1 content mismatch") + assert.Equal(t, n1.addedOn, int64(1536977237), "note 1 added_on mismatch") + assert.Equal(t, n1.editedOn, int64(1536977253), "note 1 edited_on mismatch") + assert.Equal(t, n1.public, false, "note 1 public mismatch") - testutils.AssertEqual(t, n2.uuid, "35cbcab1-6a2a-4cc8-97e0-e73bbbd54626", "note 2 uuid mismatch") - testutils.AssertNotEqual(t, n2.id, 0, "note 2 id should have been generated") - testutils.AssertEqual(t, n2.bookUUID, b1.uuid, "note 2 book_uuid mismatch") - testutils.AssertEqual(t, n2.content, "js test 1", "note 2 content mismatch") - testutils.AssertEqual(t, n2.addedOn, int64(1536977229), "note 2 added_on mismatch") - testutils.AssertEqual(t, n2.editedOn, int64(0), "note 2 edited_on mismatch") - testutils.AssertEqual(t, n2.public, false, "note 2 public mismatch") + assert.Equal(t, n2.uuid, "35cbcab1-6a2a-4cc8-97e0-e73bbbd54626", "note 2 uuid mismatch") + assert.NotEqual(t, n2.id, 0, "note 2 id should have been generated") + assert.Equal(t, n2.bookUUID, b1.uuid, "note 2 book_uuid mismatch") + assert.Equal(t, n2.content, "js test 1", "note 2 content mismatch") + assert.Equal(t, n2.addedOn, int64(1536977229), "note 2 added_on mismatch") + assert.Equal(t, n2.editedOn, int64(0), "note 2 edited_on mismatch") + assert.Equal(t, n2.public, false, "note 2 public mismatch") - testutils.AssertEqual(t, n3.uuid, "7c1fcfb2-de8b-4350-88f0-fb3cbaf6630a", "note 3 uuid mismatch") - testutils.AssertNotEqual(t, n3.id, 0, "note 3 id should have been generated") - testutils.AssertEqual(t, n3.bookUUID, b1.uuid, "note 3 book_uuid mismatch") - testutils.AssertEqual(t, n3.content, "js test 2", "note 3 content mismatch") - testutils.AssertEqual(t, n3.addedOn, int64(1536977230), "note 3 added_on mismatch") - testutils.AssertEqual(t, n3.editedOn, int64(0), "note 3 edited_on mismatch") - testutils.AssertEqual(t, n3.public, false, "note 3 public mismatch") + assert.Equal(t, n3.uuid, "7c1fcfb2-de8b-4350-88f0-fb3cbaf6630a", "note 3 uuid mismatch") + assert.NotEqual(t, n3.id, 0, "note 3 id should have been generated") + assert.Equal(t, n3.bookUUID, b1.uuid, "note 3 book_uuid mismatch") + assert.Equal(t, n3.content, "js test 2", "note 3 content mismatch") + assert.Equal(t, n3.addedOn, int64(1536977230), "note 3 added_on mismatch") + assert.Equal(t, n3.editedOn, int64(0), "note 3 edited_on mismatch") + assert.Equal(t, n3.public, false, "note 3 public mismatch") // 3. test if actions are migrated var actionCount int @@ -457,7 +472,7 @@ func TestMigrateToV8(t *testing.T) { panic(errors.Wrap(err, "counting actions")) } - testutils.AssertEqual(t, actionCount, 11, "action count mismatch") + assert.Equal(t, actionCount, 11, "action count mismatch") type actionInfo struct { uuid string @@ -513,71 +528,71 @@ func TestMigrateToV8(t *testing.T) { panic(errors.Wrap(err, "finding a11")) } - testutils.AssertEqual(t, a1.uuid, "6145c1b7-f286-4d9f-b0f6-00d274baefc6", "action 1 uuid mismatch") - testutils.AssertEqual(t, a1.schema, 1, "action 1 schema mismatch") - testutils.AssertEqual(t, a1.actionType, "add_book", "action 1 type mismatch") - testutils.AssertEqual(t, a1.data, `{"book_name":"js"}`, "action 1 data mismatch") - testutils.AssertEqual(t, a1.timestamp, 1536977229, "action 1 timestamp mismatch") + assert.Equal(t, a1.uuid, "6145c1b7-f286-4d9f-b0f6-00d274baefc6", "action 1 uuid mismatch") + assert.Equal(t, a1.schema, 1, "action 1 schema mismatch") + assert.Equal(t, a1.actionType, "add_book", "action 1 type mismatch") + assert.Equal(t, a1.data, `{"book_name":"js"}`, "action 1 data mismatch") + assert.Equal(t, a1.timestamp, 1536977229, "action 1 timestamp mismatch") - testutils.AssertEqual(t, a2.uuid, "c048a56b-179c-4f31-9995-81e9b32b7dd6", "action 2 uuid mismatch") - testutils.AssertEqual(t, a2.schema, 2, "action 2 schema mismatch") - testutils.AssertEqual(t, a2.actionType, "add_note", "action 2 type mismatch") - testutils.AssertEqual(t, a2.data, `{"note_uuid":"35cbcab1-6a2a-4cc8-97e0-e73bbbd54626","book_name":"js","content":"js test 1","public":false}`, "action 2 data mismatch") - testutils.AssertEqual(t, a2.timestamp, 1536977229, "action 2 timestamp mismatch") + assert.Equal(t, a2.uuid, "c048a56b-179c-4f31-9995-81e9b32b7dd6", "action 2 uuid mismatch") + assert.Equal(t, a2.schema, 2, "action 2 schema mismatch") + assert.Equal(t, a2.actionType, "add_note", "action 2 type mismatch") + assert.Equal(t, a2.data, `{"note_uuid":"35cbcab1-6a2a-4cc8-97e0-e73bbbd54626","book_name":"js","content":"js test 1","public":false}`, "action 2 data mismatch") + assert.Equal(t, a2.timestamp, 1536977229, "action 2 timestamp mismatch") - testutils.AssertEqual(t, a3.uuid, "f557ef48-c304-47dc-adfb-46b7306e701f", "action 3 uuid mismatch") - testutils.AssertEqual(t, a3.schema, 2, "action 3 schema mismatch") - testutils.AssertEqual(t, a3.actionType, "add_note", "action 3 type mismatch") - testutils.AssertEqual(t, a3.data, `{"note_uuid":"7c1fcfb2-de8b-4350-88f0-fb3cbaf6630a","book_name":"js","content":"js test 2","public":false}`, "action 3 data mismatch") - testutils.AssertEqual(t, a3.timestamp, 1536977230, "action 3 timestamp mismatch") + assert.Equal(t, a3.uuid, "f557ef48-c304-47dc-adfb-46b7306e701f", "action 3 uuid mismatch") + assert.Equal(t, a3.schema, 2, "action 3 schema mismatch") + assert.Equal(t, a3.actionType, "add_note", "action 3 type mismatch") + assert.Equal(t, a3.data, `{"note_uuid":"7c1fcfb2-de8b-4350-88f0-fb3cbaf6630a","book_name":"js","content":"js test 2","public":false}`, "action 3 data mismatch") + assert.Equal(t, a3.timestamp, 1536977230, "action 3 timestamp mismatch") - testutils.AssertEqual(t, a4.uuid, "8d79db34-343d-4331-ae5b-24743f17ca7f", "action 4 uuid mismatch") - testutils.AssertEqual(t, a4.schema, 2, "action 4 schema mismatch") - testutils.AssertEqual(t, a4.actionType, "add_note", "action 4 type mismatch") - testutils.AssertEqual(t, a4.data, `{"note_uuid":"b23a88ba-b291-4294-9795-86b394db5dcf","book_name":"js","content":"js test 3","public":false}`, "action 4 data mismatch") - testutils.AssertEqual(t, a4.timestamp, 1536977234, "action 4 timestamp mismatch") + assert.Equal(t, a4.uuid, "8d79db34-343d-4331-ae5b-24743f17ca7f", "action 4 uuid mismatch") + assert.Equal(t, a4.schema, 2, "action 4 schema mismatch") + assert.Equal(t, a4.actionType, "add_note", "action 4 type mismatch") + assert.Equal(t, a4.data, `{"note_uuid":"b23a88ba-b291-4294-9795-86b394db5dcf","book_name":"js","content":"js test 3","public":false}`, "action 4 data mismatch") + assert.Equal(t, a4.timestamp, 1536977234, "action 4 timestamp mismatch") - testutils.AssertEqual(t, a5.uuid, "b9c1ed4a-e6b3-41f2-983b-593ec7b8b7a1", "action 5 uuid mismatch") - testutils.AssertEqual(t, a5.schema, 1, "action 5 schema mismatch") - testutils.AssertEqual(t, a5.actionType, "add_book", "action 5 type mismatch") - testutils.AssertEqual(t, a5.data, `{"book_name":"css"}`, "action 5 data mismatch") - testutils.AssertEqual(t, a5.timestamp, 1536977237, "action 5 timestamp mismatch") + assert.Equal(t, a5.uuid, "b9c1ed4a-e6b3-41f2-983b-593ec7b8b7a1", "action 5 uuid mismatch") + assert.Equal(t, a5.schema, 1, "action 5 schema mismatch") + assert.Equal(t, a5.actionType, "add_book", "action 5 type mismatch") + assert.Equal(t, a5.data, `{"book_name":"css"}`, "action 5 data mismatch") + assert.Equal(t, a5.timestamp, 1536977237, "action 5 timestamp mismatch") - testutils.AssertEqual(t, a6.uuid, "06ed7ef0-f171-4bd7-ae8e-97b5d06a4c49", "action 6 uuid mismatch") - testutils.AssertEqual(t, a6.schema, 2, "action 6 schema mismatch") - testutils.AssertEqual(t, a6.actionType, "add_note", "action 6 type mismatch") - testutils.AssertEqual(t, a6.data, `{"note_uuid":"d69edb54-5b31-4cdd-a4a5-34f0a0bfa153","book_name":"css","content":"js test 3","public":false}`, "action 6 data mismatch") - testutils.AssertEqual(t, a6.timestamp, 1536977237, "action 6 timestamp mismatch") + assert.Equal(t, a6.uuid, "06ed7ef0-f171-4bd7-ae8e-97b5d06a4c49", "action 6 uuid mismatch") + assert.Equal(t, a6.schema, 2, "action 6 schema mismatch") + assert.Equal(t, a6.actionType, "add_note", "action 6 type mismatch") + assert.Equal(t, a6.data, `{"note_uuid":"d69edb54-5b31-4cdd-a4a5-34f0a0bfa153","book_name":"css","content":"js test 3","public":false}`, "action 6 data mismatch") + assert.Equal(t, a6.timestamp, 1536977237, "action 6 timestamp mismatch") - testutils.AssertEqual(t, a7.uuid, "7f173cef-1688-4177-a373-145fcd822b2f", "action 7 uuid mismatch") - testutils.AssertEqual(t, a7.schema, 2, "action 7 schema mismatch") - testutils.AssertEqual(t, a7.actionType, "edit_note", "action 7 type mismatch") - testutils.AssertEqual(t, a7.data, `{"note_uuid":"d69edb54-5b31-4cdd-a4a5-34f0a0bfa153","from_book":"css","to_book":null,"content":"css test 1","public":null}`, "action 7 data mismatch") - testutils.AssertEqual(t, a7.timestamp, 1536977253, "action 7 timestamp mismatch") + assert.Equal(t, a7.uuid, "7f173cef-1688-4177-a373-145fcd822b2f", "action 7 uuid mismatch") + assert.Equal(t, a7.schema, 2, "action 7 schema mismatch") + assert.Equal(t, a7.actionType, "edit_note", "action 7 type mismatch") + assert.Equal(t, a7.data, `{"note_uuid":"d69edb54-5b31-4cdd-a4a5-34f0a0bfa153","from_book":"css","to_book":null,"content":"css test 1","public":null}`, "action 7 data mismatch") + assert.Equal(t, a7.timestamp, 1536977253, "action 7 timestamp mismatch") - testutils.AssertEqual(t, a8.uuid, "64352e08-aa7a-45f4-b760-b3f38b5e11fa", "action 8 uuid mismatch") - testutils.AssertEqual(t, a8.schema, 1, "action 8 schema mismatch") - testutils.AssertEqual(t, a8.actionType, "add_book", "action 8 type mismatch") - testutils.AssertEqual(t, a8.data, `{"book_name":"sql"}`, "action 8 data mismatch") - testutils.AssertEqual(t, a8.timestamp, 1536977261, "action 8 timestamp mismatch") + assert.Equal(t, a8.uuid, "64352e08-aa7a-45f4-b760-b3f38b5e11fa", "action 8 uuid mismatch") + assert.Equal(t, a8.schema, 1, "action 8 schema mismatch") + assert.Equal(t, a8.actionType, "add_book", "action 8 type mismatch") + assert.Equal(t, a8.data, `{"book_name":"sql"}`, "action 8 data mismatch") + assert.Equal(t, a8.timestamp, 1536977261, "action 8 timestamp mismatch") - testutils.AssertEqual(t, a9.uuid, "82e20a12-bda8-45f7-ac42-b453b6daa5ec", "action 9 uuid mismatch") - testutils.AssertEqual(t, a9.schema, 2, "action 9 schema mismatch") - testutils.AssertEqual(t, a9.actionType, "add_note", "action 9 type mismatch") - testutils.AssertEqual(t, a9.data, `{"note_uuid":"2f47d390-685b-4b84-89ac-704c6fb8d3fb","book_name":"sql","content":"blah","public":false}`, "action 9 data mismatch") - testutils.AssertEqual(t, a9.timestamp, 1536977261, "action 9 timestamp mismatch") + assert.Equal(t, a9.uuid, "82e20a12-bda8-45f7-ac42-b453b6daa5ec", "action 9 uuid mismatch") + assert.Equal(t, a9.schema, 2, "action 9 schema mismatch") + assert.Equal(t, a9.actionType, "add_note", "action 9 type mismatch") + assert.Equal(t, a9.data, `{"note_uuid":"2f47d390-685b-4b84-89ac-704c6fb8d3fb","book_name":"sql","content":"blah","public":false}`, "action 9 data mismatch") + assert.Equal(t, a9.timestamp, 1536977261, "action 9 timestamp mismatch") - testutils.AssertEqual(t, a10.uuid, "a29055f4-ace4-44fd-8800-3396edbccaef", "action 10 uuid mismatch") - testutils.AssertEqual(t, a10.schema, 1, "action 10 schema mismatch") - testutils.AssertEqual(t, a10.actionType, "remove_book", "action 10 type mismatch") - testutils.AssertEqual(t, a10.data, `{"book_name":"sql"}`, "action 10 data mismatch") - testutils.AssertEqual(t, a10.timestamp, 1536977268, "action 10 timestamp mismatch") + assert.Equal(t, a10.uuid, "a29055f4-ace4-44fd-8800-3396edbccaef", "action 10 uuid mismatch") + assert.Equal(t, a10.schema, 1, "action 10 schema mismatch") + assert.Equal(t, a10.actionType, "remove_book", "action 10 type mismatch") + assert.Equal(t, a10.data, `{"book_name":"sql"}`, "action 10 data mismatch") + assert.Equal(t, a10.timestamp, 1536977268, "action 10 timestamp mismatch") - testutils.AssertEqual(t, a11.uuid, "871a5562-1bd0-43c1-b550-5bbb727ac7c4", "action 11 uuid mismatch") - testutils.AssertEqual(t, a11.schema, 1, "action 11 schema mismatch") - testutils.AssertEqual(t, a11.actionType, "remove_note", "action 11 type mismatch") - testutils.AssertEqual(t, a11.data, `{"note_uuid":"b23a88ba-b291-4294-9795-86b394db5dcf","book_name":"js"}`, "action 11 data mismatch") - testutils.AssertEqual(t, a11.timestamp, 1536977274, "action 11 timestamp mismatch") + assert.Equal(t, a11.uuid, "871a5562-1bd0-43c1-b550-5bbb727ac7c4", "action 11 uuid mismatch") + assert.Equal(t, a11.schema, 1, "action 11 schema mismatch") + assert.Equal(t, a11.actionType, "remove_note", "action 11 type mismatch") + assert.Equal(t, a11.data, `{"note_uuid":"b23a88ba-b291-4294-9795-86b394db5dcf","book_name":"js"}`, "action 11 data mismatch") + assert.Equal(t, a11.timestamp, 1536977274, "action 11 timestamp mismatch") // 3. test if system is migrated var systemCount int @@ -586,7 +601,7 @@ func TestMigrateToV8(t *testing.T) { panic(errors.Wrap(err, "counting system")) } - testutils.AssertEqual(t, systemCount, 3, "action count mismatch") + assert.Equal(t, systemCount, 3, "action count mismatch") var lastUpgrade, lastAction, bookmark int err = db.QueryRow("SELECT value FROM system WHERE key = ?", "last_upgrade").Scan(&lastUpgrade) @@ -602,7 +617,7 @@ func TestMigrateToV8(t *testing.T) { panic(errors.Wrap(err, "finding bookmark")) } - testutils.AssertEqual(t, lastUpgrade, 1536977220, "last_upgrade mismatch") - testutils.AssertEqual(t, lastAction, 1536977274, "last_action mismatch") - testutils.AssertEqual(t, bookmark, 9, "bookmark mismatch") + assert.Equal(t, lastUpgrade, 1536977220, "last_upgrade mismatch") + assert.Equal(t, lastAction, 1536977274, "last_action mismatch") + assert.Equal(t, bookmark, 9, "bookmark mismatch") } diff --git a/cli/migrate/migrate.go b/pkg/cli/migrate/migrate.go similarity index 84% rename from cli/migrate/migrate.go rename to pkg/cli/migrate/migrate.go index f8276cd9..edf0c893 100644 --- a/cli/migrate/migrate.go +++ b/pkg/cli/migrate/migrate.go @@ -20,8 +20,9 @@ package migrate import ( "database/sql" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" + "github.com/dnote/dnote/pkg/cli/consts" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/log" "github.com/pkg/errors" ) @@ -52,7 +53,7 @@ var RemoteSequence = []migration{ rm1, } -func initSchema(ctx infra.DnoteCtx, schemaKey string) (int, error) { +func initSchema(ctx context.DnoteCtx, schemaKey string) (int, error) { // schemaVersion is the index of the latest run migration in the sequence schemaVersion := 0 @@ -67,17 +68,17 @@ func initSchema(ctx infra.DnoteCtx, schemaKey string) (int, error) { func getSchemaKey(mode int) (string, error) { if mode == LocalMode { - return infra.SystemSchema, nil + return consts.SystemSchema, nil } if mode == RemoteMode { - return infra.SystemRemoteSchema, nil + return consts.SystemRemoteSchema, nil } return "", errors.Errorf("unsupported migration type '%d'", mode) } -func getSchema(ctx infra.DnoteCtx, schemaKey string) (int, error) { +func getSchema(ctx context.DnoteCtx, schemaKey string) (int, error) { var ret int db := ctx.DB @@ -95,7 +96,7 @@ func getSchema(ctx infra.DnoteCtx, schemaKey string) (int, error) { return ret, nil } -func execute(ctx infra.DnoteCtx, m migration, schemaKey string) error { +func execute(ctx context.DnoteCtx, m migration, schemaKey string) error { log.Debug("running migration %s\n", m.name) tx, err := ctx.DB.Begin() @@ -128,7 +129,7 @@ func execute(ctx infra.DnoteCtx, m migration, schemaKey string) error { } // Run performs unrun migrations -func Run(ctx infra.DnoteCtx, migrations []migration, mode int) error { +func Run(ctx context.DnoteCtx, migrations []migration, mode int) error { schemaKey, err := getSchemaKey(mode) if err != nil { return errors.Wrap(err, "getting schema key") @@ -139,7 +140,7 @@ func Run(ctx infra.DnoteCtx, migrations []migration, mode int) error { return errors.Wrap(err, "getting the current schema") } - log.Debug("current schema: %s %d of %d\n", infra.SystemSchema, schema, len(migrations)) + log.Debug("current schema: %s %d of %d\n", consts.SystemSchema, schema, len(migrations)) toRun := migrations[schema:] diff --git a/pkg/cli/migrate/migrate_test.go b/pkg/cli/migrate/migrate_test.go new file mode 100644 index 00000000..cdb4137b --- /dev/null +++ b/pkg/cli/migrate/migrate_test.go @@ -0,0 +1,1134 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +package migrate + +import ( + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/dnote/actions" + "github.com/dnote/dnote/pkg/assert" + "github.com/dnote/dnote/pkg/cli/consts" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/testutils" + "github.com/dnote/dnote/pkg/cli/utils" + "github.com/pkg/errors" +) + +func TestExecute_bump_schema(t *testing.T) { + testCases := []struct { + schemaKey string + }{ + { + schemaKey: consts.SystemSchema, + }, + { + schemaKey: consts.SystemRemoteSchema, + }, + } + + for _, tc := range testCases { + func() { + // set up + opts := database.TestDBOptions{SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + + database.MustExec(t, "inserting a schema", db, "INSERT INTO system (key, value) VALUES (?, ?)", tc.schemaKey, 8) + + m1 := migration{ + name: "noop", + run: func(ctx context.DnoteCtx, db *database.DB) error { + return nil + }, + } + m2 := migration{ + name: "noop", + run: func(ctx context.DnoteCtx, db *database.DB) error { + return nil + }, + } + + // execute + err := execute(ctx, m1, tc.schemaKey) + if err != nil { + t.Fatal(errors.Wrap(err, "failed to execute")) + } + err = execute(ctx, m2, tc.schemaKey) + if err != nil { + t.Fatal(errors.Wrap(err, "failed to execute")) + } + + // test + var schema int + database.MustScan(t, "getting schema", db.QueryRow("SELECT value FROM system WHERE key = ?", tc.schemaKey), &schema) + assert.Equal(t, schema, 10, "schema was not incremented properly") + }() + } +} + +func TestRun_nonfresh(t *testing.T) { + testCases := []struct { + mode int + schemaKey string + }{ + { + mode: LocalMode, + schemaKey: consts.SystemSchema, + }, + { + mode: RemoteMode, + schemaKey: consts.SystemRemoteSchema, + }, + } + + for _, tc := range testCases { + func() { + // set up + opts := database.TestDBOptions{SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + database.MustExec(t, "inserting a schema", db, "INSERT INTO system (key, value) VALUES (?, ?)", tc.schemaKey, 2) + database.MustExec(t, "creating a temporary table for testing", db, + "CREATE TABLE migrate_run_test ( name string )") + + sequence := []migration{ + migration{ + name: "v1", + run: func(ctx context.DnoteCtx, db *database.DB) error { + database.MustExec(t, "marking v1 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v1") + return nil + }, + }, + migration{ + name: "v2", + run: func(ctx context.DnoteCtx, db *database.DB) error { + database.MustExec(t, "marking v2 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v2") + return nil + }, + }, + migration{ + name: "v3", + run: func(ctx context.DnoteCtx, db *database.DB) error { + database.MustExec(t, "marking v3 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v3") + return nil + }, + }, + migration{ + name: "v4", + run: func(ctx context.DnoteCtx, db *database.DB) error { + database.MustExec(t, "marking v4 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v4") + return nil + }, + }, + } + + // execute + err := Run(ctx, sequence, tc.mode) + if err != nil { + t.Fatal(errors.Wrap(err, "failed to run")) + } + + // test + var schema int + database.MustScan(t, fmt.Sprintf("getting schema for %s", tc.schemaKey), db.QueryRow("SELECT value FROM system WHERE key = ?", tc.schemaKey), &schema) + assert.Equal(t, schema, 4, fmt.Sprintf("schema was not updated for %s", tc.schemaKey)) + + var testRunCount int + database.MustScan(t, "counting test runs", db.QueryRow("SELECT count(*) FROM migrate_run_test"), &testRunCount) + assert.Equal(t, testRunCount, 2, "test run count mismatch") + + var testRun1, testRun2 string + database.MustScan(t, "finding test run 1", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v3"), &testRun1) + database.MustScan(t, "finding test run 2", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v4"), &testRun2) + }() + } +} + +func TestRun_fresh(t *testing.T) { + testCases := []struct { + mode int + schemaKey string + }{ + { + mode: LocalMode, + schemaKey: consts.SystemSchema, + }, + { + mode: RemoteMode, + schemaKey: consts.SystemRemoteSchema, + }, + } + + for _, tc := range testCases { + func() { + // set up + opts := database.TestDBOptions{SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + + database.MustExec(t, "creating a temporary table for testing", db, + "CREATE TABLE migrate_run_test ( name string )") + + sequence := []migration{ + migration{ + name: "v1", + run: func(ctx context.DnoteCtx, db *database.DB) error { + database.MustExec(t, "marking v1 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v1") + return nil + }, + }, + migration{ + name: "v2", + run: func(ctx context.DnoteCtx, db *database.DB) error { + database.MustExec(t, "marking v2 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v2") + return nil + }, + }, + migration{ + name: "v3", + run: func(ctx context.DnoteCtx, db *database.DB) error { + database.MustExec(t, "marking v3 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v3") + return nil + }, + }, + } + + // execute + err := Run(ctx, sequence, tc.mode) + if err != nil { + t.Fatal(errors.Wrap(err, "failed to run")) + } + + // test + var schema int + database.MustScan(t, "getting schema", db.QueryRow("SELECT value FROM system WHERE key = ?", tc.schemaKey), &schema) + assert.Equal(t, schema, 3, "schema was not updated") + + var testRunCount int + database.MustScan(t, "counting test runs", db.QueryRow("SELECT count(*) FROM migrate_run_test"), &testRunCount) + assert.Equal(t, testRunCount, 3, "test run count mismatch") + + var testRun1, testRun2, testRun3 string + database.MustScan(t, "finding test run 1", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v1"), &testRun1) + database.MustScan(t, "finding test run 2", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v2"), &testRun2) + database.MustScan(t, "finding test run 2", db.QueryRow("SELECT name FROM migrate_run_test WHERE name = ?", "v3"), &testRun3) + }() + } +} + +func TestRun_up_to_date(t *testing.T) { + testCases := []struct { + mode int + schemaKey string + }{ + { + mode: LocalMode, + schemaKey: consts.SystemSchema, + }, + { + mode: RemoteMode, + schemaKey: consts.SystemRemoteSchema, + }, + } + + for _, tc := range testCases { + func() { + // set up + opts := database.TestDBOptions{SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + + database.MustExec(t, "creating a temporary table for testing", db, + "CREATE TABLE migrate_run_test ( name string )") + + database.MustExec(t, "inserting a schema", db, "INSERT INTO system (key, value) VALUES (?, ?)", tc.schemaKey, 3) + + sequence := []migration{ + migration{ + name: "v1", + run: func(ctx context.DnoteCtx, db *database.DB) error { + database.MustExec(t, "marking v1 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v1") + return nil + }, + }, + migration{ + name: "v2", + run: func(ctx context.DnoteCtx, db *database.DB) error { + database.MustExec(t, "marking v2 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v2") + return nil + }, + }, + migration{ + name: "v3", + run: func(ctx context.DnoteCtx, db *database.DB) error { + database.MustExec(t, "marking v3 completed", db, "INSERT INTO migrate_run_test (name) VALUES (?)", "v3") + return nil + }, + }, + } + + // execute + err := Run(ctx, sequence, tc.mode) + if err != nil { + t.Fatal(errors.Wrap(err, "failed to run")) + } + + // test + var schema int + database.MustScan(t, "getting schema", db.QueryRow("SELECT value FROM system WHERE key = ?", tc.schemaKey), &schema) + assert.Equal(t, schema, 3, "schema was not updated") + + var testRunCount int + database.MustScan(t, "counting test runs", db.QueryRow("SELECT count(*) FROM migrate_run_test"), &testRunCount) + assert.Equal(t, testRunCount, 0, "test run count mismatch") + }() + } +} + +func TestLocalMigration1(t *testing.T) { + // set up + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-1-pre-schema.sql", SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + data := testutils.MustMarshalJSON(t, actions.AddBookDataV1{BookName: "js"}) + a1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting action", db, + "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a1UUID, 1, "add_book", string(data), 1537829463) + + data = testutils.MustMarshalJSON(t, actions.EditNoteDataV1{NoteUUID: "note-1-uuid", FromBook: "js", ToBook: "", Content: "note 1"}) + a2UUID := utils.GenerateUUID() + database.MustExec(t, "inserting action", db, + "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a2UUID, 1, "edit_note", string(data), 1537829463) + + data = testutils.MustMarshalJSON(t, actions.EditNoteDataV1{NoteUUID: "note-2-uuid", FromBook: "js", ToBook: "", Content: "note 2"}) + a3UUID := utils.GenerateUUID() + database.MustExec(t, "inserting action", db, + "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a3UUID, 1, "edit_note", string(data), 1537829463) + + // Execute + tx, err := db.Begin() + if err != nil { + t.Fatal(errors.Wrap(err, "beginning a transaction")) + } + + err = lm1.run(ctx, tx) + if err != nil { + tx.Rollback() + t.Fatal(errors.Wrap(err, "failed to run")) + } + + tx.Commit() + + // Test + var actionCount int + database.MustScan(t, "counting actions", db.QueryRow("SELECT count(*) FROM actions"), &actionCount) + assert.Equal(t, actionCount, 3, "action count mismatch") + + var a1, a2, a3 actions.Action + database.MustScan(t, "getting action 1", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a1UUID), + &a1.Schema, &a1.Type, &a1.Data, &a1.Timestamp) + database.MustScan(t, "getting action 2", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a2UUID), + &a2.Schema, &a2.Type, &a2.Data, &a2.Timestamp) + database.MustScan(t, "getting action 3", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a3UUID), + &a3.Schema, &a3.Type, &a3.Data, &a3.Timestamp) + + var a1Data actions.AddBookDataV1 + var a2Data, a3Data actions.EditNoteDataV3 + testutils.MustUnmarshalJSON(t, a1.Data, &a1Data) + testutils.MustUnmarshalJSON(t, a2.Data, &a2Data) + testutils.MustUnmarshalJSON(t, a3.Data, &a3Data) + + assert.Equal(t, a1.Schema, 1, "a1 schema mismatch") + assert.Equal(t, a1.Type, "add_book", "a1 type mismatch") + assert.Equal(t, a1.Timestamp, int64(1537829463), "a1 timestamp mismatch") + assert.Equal(t, a1Data.BookName, "js", "a1 data book_name mismatch") + + assert.Equal(t, a2.Schema, 3, "a2 schema mismatch") + assert.Equal(t, a2.Type, "edit_note", "a2 type mismatch") + assert.Equal(t, a2.Timestamp, int64(1537829463), "a2 timestamp mismatch") + assert.Equal(t, a2Data.NoteUUID, "note-1-uuid", "a2 data note_uuid mismatch") + assert.Equal(t, a2Data.BookName, (*string)(nil), "a2 data book_name mismatch") + assert.Equal(t, *a2Data.Content, "note 1", "a2 data content mismatch") + assert.Equal(t, *a2Data.Public, false, "a2 data public mismatch") + + assert.Equal(t, a3.Schema, 3, "a3 schema mismatch") + assert.Equal(t, a3.Type, "edit_note", "a3 type mismatch") + assert.Equal(t, a3.Timestamp, int64(1537829463), "a3 timestamp mismatch") + assert.Equal(t, a3Data.NoteUUID, "note-2-uuid", "a3 data note_uuid mismatch") + assert.Equal(t, a3Data.BookName, (*string)(nil), "a3 data book_name mismatch") + assert.Equal(t, *a3Data.Content, "note 2", "a3 data content mismatch") + assert.Equal(t, *a3Data.Public, false, "a3 data public mismatch") +} + +func TestLocalMigration2(t *testing.T) { + // set up + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-1-pre-schema.sql", SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + c1 := "note 1 - v1" + c2 := "note 1 - v2" + css := "css" + + b1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting css book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "css") + + data := testutils.MustMarshalJSON(t, actions.AddNoteDataV2{NoteUUID: "note-1-uuid", BookName: "js", Content: "note 1", Public: false}) + a1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting action", db, + "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a1UUID, 2, "add_note", string(data), 1537829463) + + data = testutils.MustMarshalJSON(t, actions.EditNoteDataV2{NoteUUID: "note-1-uuid", FromBook: "js", ToBook: nil, Content: &c1, Public: nil}) + a2UUID := utils.GenerateUUID() + database.MustExec(t, "inserting action", db, + "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a2UUID, 2, "edit_note", string(data), 1537829463) + + data = testutils.MustMarshalJSON(t, actions.EditNoteDataV2{NoteUUID: "note-1-uuid", FromBook: "js", ToBook: &css, Content: &c2, Public: nil}) + a3UUID := utils.GenerateUUID() + database.MustExec(t, "inserting action", db, + "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a3UUID, 2, "edit_note", string(data), 1537829463) + + // Execute + tx, err := db.Begin() + if err != nil { + t.Fatal(errors.Wrap(err, "beginning a transaction")) + } + + err = lm2.run(ctx, tx) + if err != nil { + tx.Rollback() + t.Fatal(errors.Wrap(err, "failed to run")) + } + + tx.Commit() + + // Test + var actionCount int + database.MustScan(t, "counting actions", db.QueryRow("SELECT count(*) FROM actions"), &actionCount) + assert.Equal(t, actionCount, 3, "action count mismatch") + + var a1, a2, a3 actions.Action + database.MustScan(t, "getting action 1", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a1UUID), + &a1.Schema, &a1.Type, &a1.Data, &a1.Timestamp) + database.MustScan(t, "getting action 2", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a2UUID), + &a2.Schema, &a2.Type, &a2.Data, &a2.Timestamp) + database.MustScan(t, "getting action 3", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a3UUID), + &a3.Schema, &a3.Type, &a3.Data, &a3.Timestamp) + + var a1Data actions.AddNoteDataV2 + var a2Data, a3Data actions.EditNoteDataV3 + testutils.MustUnmarshalJSON(t, a1.Data, &a1Data) + testutils.MustUnmarshalJSON(t, a2.Data, &a2Data) + testutils.MustUnmarshalJSON(t, a3.Data, &a3Data) + + assert.Equal(t, a1.Schema, 2, "a1 schema mismatch") + assert.Equal(t, a1.Type, "add_note", "a1 type mismatch") + assert.Equal(t, a1.Timestamp, int64(1537829463), "a1 timestamp mismatch") + assert.Equal(t, a1Data.NoteUUID, "note-1-uuid", "a1 data note_uuid mismatch") + assert.Equal(t, a1Data.BookName, "js", "a1 data book_name mismatch") + assert.Equal(t, a1Data.Public, false, "a1 data public mismatch") + + assert.Equal(t, a2.Schema, 3, "a2 schema mismatch") + assert.Equal(t, a2.Type, "edit_note", "a2 type mismatch") + assert.Equal(t, a2.Timestamp, int64(1537829463), "a2 timestamp mismatch") + assert.Equal(t, a2Data.NoteUUID, "note-1-uuid", "a2 data note_uuid mismatch") + assert.Equal(t, a2Data.BookName, (*string)(nil), "a2 data book_name mismatch") + assert.Equal(t, *a2Data.Content, c1, "a2 data content mismatch") + assert.Equal(t, a2Data.Public, (*bool)(nil), "a2 data public mismatch") + + assert.Equal(t, a3.Schema, 3, "a3 schema mismatch") + assert.Equal(t, a3.Type, "edit_note", "a3 type mismatch") + assert.Equal(t, a3.Timestamp, int64(1537829463), "a3 timestamp mismatch") + assert.Equal(t, a3Data.NoteUUID, "note-1-uuid", "a3 data note_uuid mismatch") + assert.Equal(t, *a3Data.BookName, "css", "a3 data book_name mismatch") + assert.Equal(t, *a3Data.Content, c2, "a3 data content mismatch") + assert.Equal(t, a3Data.Public, (*bool)(nil), "a3 data public mismatch") +} + +func TestLocalMigration3(t *testing.T) { + // set up + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-1-pre-schema.sql", SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + data := testutils.MustMarshalJSON(t, actions.AddNoteDataV2{NoteUUID: "note-1-uuid", BookName: "js", Content: "note 1", Public: false}) + a1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting action", db, + "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a1UUID, 2, "add_note", string(data), 1537829463) + + data = testutils.MustMarshalJSON(t, actions.RemoveNoteDataV1{NoteUUID: "note-1-uuid", BookName: "js"}) + a2UUID := utils.GenerateUUID() + database.MustExec(t, "inserting action", db, + "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a2UUID, 1, "remove_note", string(data), 1537829463) + + data = testutils.MustMarshalJSON(t, actions.RemoveNoteDataV1{NoteUUID: "note-2-uuid", BookName: "js"}) + a3UUID := utils.GenerateUUID() + database.MustExec(t, "inserting action", db, + "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a3UUID, 1, "remove_note", string(data), 1537829463) + + // Execute + tx, err := db.Begin() + if err != nil { + t.Fatal(errors.Wrap(err, "beginning a transaction")) + } + + err = lm3.run(ctx, tx) + if err != nil { + tx.Rollback() + t.Fatal(errors.Wrap(err, "failed to run")) + } + + tx.Commit() + + // Test + var actionCount int + database.MustScan(t, "counting actions", db.QueryRow("SELECT count(*) FROM actions"), &actionCount) + assert.Equal(t, actionCount, 3, "action count mismatch") + + var a1, a2, a3 actions.Action + database.MustScan(t, "getting action 1", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a1UUID), + &a1.Schema, &a1.Type, &a1.Data, &a1.Timestamp) + database.MustScan(t, "getting action 2", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a2UUID), + &a2.Schema, &a2.Type, &a2.Data, &a2.Timestamp) + database.MustScan(t, "getting action 3", db.QueryRow("SELECT schema, type, data, timestamp FROM actions WHERE uuid = ?", a3UUID), + &a3.Schema, &a3.Type, &a3.Data, &a3.Timestamp) + + var a1Data actions.AddNoteDataV2 + var a2Data, a3Data actions.RemoveNoteDataV2 + testutils.MustUnmarshalJSON(t, a1.Data, &a1Data) + testutils.MustUnmarshalJSON(t, a2.Data, &a2Data) + testutils.MustUnmarshalJSON(t, a3.Data, &a3Data) + + assert.Equal(t, a1.Schema, 2, "a1 schema mismatch") + assert.Equal(t, a1.Type, "add_note", "a1 type mismatch") + assert.Equal(t, a1.Timestamp, int64(1537829463), "a1 timestamp mismatch") + assert.Equal(t, a1Data.NoteUUID, "note-1-uuid", "a1 data note_uuid mismatch") + assert.Equal(t, a1Data.BookName, "js", "a1 data book_name mismatch") + assert.Equal(t, a1Data.Content, "note 1", "a1 data content mismatch") + assert.Equal(t, a1Data.Public, false, "a1 data public mismatch") + + assert.Equal(t, a2.Schema, 2, "a2 schema mismatch") + assert.Equal(t, a2.Type, "remove_note", "a2 type mismatch") + assert.Equal(t, a2.Timestamp, int64(1537829463), "a2 timestamp mismatch") + assert.Equal(t, a2Data.NoteUUID, "note-1-uuid", "a2 data note_uuid mismatch") + + assert.Equal(t, a3.Schema, 2, "a3 schema mismatch") + assert.Equal(t, a3.Type, "remove_note", "a3 type mismatch") + assert.Equal(t, a3.Timestamp, int64(1537829463), "a3 timestamp mismatch") + assert.Equal(t, a3Data.NoteUUID, "note-2-uuid", "a3 data note_uuid mismatch") +} + +func TestLocalMigration4(t *testing.T) { + // set up + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-1-pre-schema.sql", SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + + b1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting css book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "css") + n1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting css note", db, "INSERT INTO notes (uuid, book_uuid, content, added_on) VALUES (?, ?, ?, ?)", n1UUID, b1UUID, "n1 content", time.Now().UnixNano()) + + // Execute + tx, err := db.Begin() + if err != nil { + t.Fatal(errors.Wrap(err, "beginning a transaction")) + } + + err = lm4.run(ctx, tx) + if err != nil { + tx.Rollback() + t.Fatal(errors.Wrap(err, "failed to run")) + } + + tx.Commit() + + // Test + var n1Dirty, b1Dirty bool + var n1Deleted, b1Deleted bool + var n1USN, b1USN int + database.MustScan(t, "scanning the newly added dirty flag of n1", db.QueryRow("SELECT dirty, deleted, usn FROM notes WHERE uuid = ?", n1UUID), &n1Dirty, &n1Deleted, &n1USN) + database.MustScan(t, "scanning the newly added dirty flag of b1", db.QueryRow("SELECT dirty, deleted, usn FROM books WHERE uuid = ?", b1UUID), &b1Dirty, &b1Deleted, &b1USN) + + assert.Equal(t, n1Dirty, false, "n1 dirty flag should be false by default") + assert.Equal(t, b1Dirty, false, "b1 dirty flag should be false by default") + + assert.Equal(t, n1Deleted, false, "n1 deleted flag should be false by default") + assert.Equal(t, b1Deleted, false, "b1 deleted flag should be false by default") + + assert.Equal(t, n1USN, 0, "n1 usn flag should be 0 by default") + assert.Equal(t, b1USN, 0, "b1 usn flag should be 0 by default") +} + +func TestLocalMigration5(t *testing.T) { + // set up + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-5-pre-schema.sql", SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + + b1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting css book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "css") + b2UUID := utils.GenerateUUID() + database.MustExec(t, "inserting js book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "js") + + n1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting css note", db, "INSERT INTO notes (uuid, book_uuid, content, added_on) VALUES (?, ?, ?, ?)", n1UUID, b1UUID, "n1 content", time.Now().UnixNano()) + n2UUID := utils.GenerateUUID() + database.MustExec(t, "inserting css note", db, "INSERT INTO notes (uuid, book_uuid, content, added_on) VALUES (?, ?, ?, ?)", n2UUID, b1UUID, "n2 content", time.Now().UnixNano()) + n3UUID := utils.GenerateUUID() + database.MustExec(t, "inserting css note", db, "INSERT INTO notes (uuid, book_uuid, content, added_on) VALUES (?, ?, ?, ?)", n3UUID, b1UUID, "n3 content", time.Now().UnixNano()) + + data := testutils.MustMarshalJSON(t, actions.AddBookDataV1{BookName: "js"}) + database.MustExec(t, "inserting a1", db, + "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", "a1-uuid", 1, "add_book", string(data), 1537829463) + + data = testutils.MustMarshalJSON(t, actions.AddNoteDataV2{NoteUUID: n1UUID, BookName: "css", Content: "n1 content", Public: false}) + database.MustExec(t, "inserting a2", db, + "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", "a2-uuid", 1, "add_note", string(data), 1537829463) + + updatedContent := "updated content" + data = testutils.MustMarshalJSON(t, actions.EditNoteDataV3{NoteUUID: n2UUID, BookName: (*string)(nil), Content: &updatedContent, Public: (*bool)(nil)}) + database.MustExec(t, "inserting a3", db, + "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", "a3-uuid", 1, "edit_note", string(data), 1537829463) + + // Execute + tx, err := db.Begin() + if err != nil { + t.Fatal(errors.Wrap(err, "beginning a transaction")) + } + + err = lm5.run(ctx, tx) + if err != nil { + tx.Rollback() + t.Fatal(errors.Wrap(err, "failed to run")) + } + + tx.Commit() + + // Test + var b1Dirty, b2Dirty, n1Dirty, n2Dirty, n3Dirty bool + database.MustScan(t, "scanning the newly added dirty flag of b1", db.QueryRow("SELECT dirty FROM books WHERE uuid = ?", b1UUID), &b1Dirty) + database.MustScan(t, "scanning the newly added dirty flag of b2", db.QueryRow("SELECT dirty FROM books WHERE uuid = ?", b2UUID), &b2Dirty) + database.MustScan(t, "scanning the newly added dirty flag of n1", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", n1UUID), &n1Dirty) + database.MustScan(t, "scanning the newly added dirty flag of n2", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", n2UUID), &n2Dirty) + database.MustScan(t, "scanning the newly added dirty flag of n3", db.QueryRow("SELECT dirty FROM notes WHERE uuid = ?", n3UUID), &n3Dirty) + + assert.Equal(t, b1Dirty, false, "b1 dirty flag should be false by default") + assert.Equal(t, b2Dirty, true, "b2 dirty flag should be false by default") + assert.Equal(t, n1Dirty, true, "n1 dirty flag should be false by default") + assert.Equal(t, n2Dirty, true, "n2 dirty flag should be false by default") + assert.Equal(t, n3Dirty, false, "n3 dirty flag should be false by default") +} + +func TestLocalMigration6(t *testing.T) { + // set up + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-5-pre-schema.sql", SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + + data := testutils.MustMarshalJSON(t, actions.AddBookDataV1{BookName: "js"}) + a1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting action", db, + "INSERT INTO actions (uuid, schema, type, data, timestamp) VALUES (?, ?, ?, ?, ?)", a1UUID, 1, "add_book", string(data), 1537829463) + + // Execute + tx, err := db.Begin() + if err != nil { + t.Fatal(errors.Wrap(err, "beginning a transaction")) + } + + err = lm5.run(ctx, tx) + if err != nil { + tx.Rollback() + t.Fatal(errors.Wrap(err, "failed to run")) + } + + tx.Commit() + + // Test + var count int + err = db.QueryRow("SELECT name FROM sqlite_master WHERE type='table' AND name = ?;", "actions").Scan(&count) + assert.Equal(t, count, 0, "actions table should have been deleted") +} + +func TestLocalMigration7_trash(t *testing.T) { + // set up + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-7-pre-schema.sql", SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + + b1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting trash book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "trash") + + // Execute + tx, err := db.Begin() + if err != nil { + t.Fatal(errors.Wrap(err, "beginning a transaction")) + } + + err = lm7.run(ctx, tx) + if err != nil { + tx.Rollback() + t.Fatal(errors.Wrap(err, "failed to run")) + } + + tx.Commit() + + // Test + var b1Label string + var b1Dirty bool + database.MustScan(t, "scanning b1 label", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty) + assert.Equal(t, b1Label, "trash (2)", "b1 label was not migrated") + assert.Equal(t, b1Dirty, true, "b1 was not marked dirty") +} + +func TestLocalMigration7_conflicts(t *testing.T) { + // set up + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-7-pre-schema.sql", SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + + b1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "conflicts") + + // Execute + tx, err := db.Begin() + if err != nil { + t.Fatal(errors.Wrap(err, "beginning a transaction")) + } + + err = lm7.run(ctx, tx) + if err != nil { + tx.Rollback() + t.Fatal(errors.Wrap(err, "failed to run")) + } + + tx.Commit() + + // Test + var b1Label string + var b1Dirty bool + database.MustScan(t, "scanning b1 label", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty) + assert.Equal(t, b1Label, "conflicts (2)", "b1 label was not migrated") + assert.Equal(t, b1Dirty, true, "b1 was not marked dirty") +} + +func TestLocalMigration7_conflicts_dup(t *testing.T) { + // set up + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-7-pre-schema.sql", SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + + b1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "conflicts") + b2UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "conflicts (2)") + + // Execute + tx, err := db.Begin() + if err != nil { + t.Fatal(errors.Wrap(err, "beginning a transaction")) + } + + err = lm7.run(ctx, tx) + if err != nil { + tx.Rollback() + t.Fatal(errors.Wrap(err, "failed to run")) + } + + tx.Commit() + + // Test + var b1Label, b2Label string + var b1Dirty, b2Dirty bool + database.MustScan(t, "scanning b1 label", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty) + database.MustScan(t, "scanning b2 label", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b2UUID), &b2Label, &b2Dirty) + assert.Equal(t, b1Label, "conflicts (3)", "b1 label was not migrated") + assert.Equal(t, b2Label, "conflicts (2)", "b1 label was not migrated") + assert.Equal(t, b1Dirty, true, "b1 was not marked dirty") + assert.Equal(t, b2Dirty, false, "b2 should not have been marked dirty") +} + +func TestLocalMigration8(t *testing.T) { + // set up + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-8-pre-schema.sql", SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + + b1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1") + + n1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting n1", db, `INSERT INTO notes + (id, uuid, book_uuid, content, added_on, edited_on, public, dirty, usn, deleted) VALUES + (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, 1, n1UUID, b1UUID, "n1 Body", 1, 2, true, true, 20, false) + n2UUID := utils.GenerateUUID() + database.MustExec(t, "inserting n2", db, `INSERT INTO notes + (id, uuid, book_uuid, content, added_on, edited_on, public, dirty, usn, deleted) VALUES + (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, 2, n2UUID, b1UUID, "", 3, 4, false, true, 21, true) + + // Execute + tx, err := db.Begin() + if err != nil { + t.Fatal(errors.Wrap(err, "beginning a transaction")) + } + + err = lm8.run(ctx, tx) + if err != nil { + tx.Rollback() + t.Fatal(errors.Wrap(err, "failed to run")) + } + + tx.Commit() + + // Test + var n1BookUUID, n1Body string + var n1AddedOn, n1EditedOn int64 + var n1USN int + var n1Public, n1Dirty, n1Deleted bool + database.MustScan(t, "scanning n1", db.QueryRow("SELECT book_uuid, body, added_on, edited_on, usn, public, dirty, deleted FROM notes WHERE uuid = ?", n1UUID), &n1BookUUID, &n1Body, &n1AddedOn, &n1EditedOn, &n1USN, &n1Public, &n1Dirty, &n1Deleted) + + var n2BookUUID, n2Body string + var n2AddedOn, n2EditedOn int64 + var n2USN int + var n2Public, n2Dirty, n2Deleted bool + database.MustScan(t, "scanning n2", db.QueryRow("SELECT book_uuid, body, added_on, edited_on, usn, public, dirty, deleted FROM notes WHERE uuid = ?", n2UUID), &n2BookUUID, &n2Body, &n2AddedOn, &n2EditedOn, &n2USN, &n2Public, &n2Dirty, &n2Deleted) + + assert.Equal(t, n1BookUUID, b1UUID, "n1 BookUUID mismatch") + assert.Equal(t, n1Body, "n1 Body", "n1 Body mismatch") + assert.Equal(t, n1AddedOn, int64(1), "n1 AddedOn mismatch") + assert.Equal(t, n1EditedOn, int64(2), "n1 EditedOn mismatch") + assert.Equal(t, n1USN, 20, "n1 USN mismatch") + assert.Equal(t, n1Public, true, "n1 Public mismatch") + assert.Equal(t, n1Dirty, true, "n1 Dirty mismatch") + assert.Equal(t, n1Deleted, false, "n1 Deleted mismatch") + + assert.Equal(t, n2BookUUID, b1UUID, "n2 BookUUID mismatch") + assert.Equal(t, n2Body, "", "n2 Body mismatch") + assert.Equal(t, n2AddedOn, int64(3), "n2 AddedOn mismatch") + assert.Equal(t, n2EditedOn, int64(4), "n2 EditedOn mismatch") + assert.Equal(t, n2USN, 21, "n2 USN mismatch") + assert.Equal(t, n2Public, false, "n2 Public mismatch") + assert.Equal(t, n2Dirty, true, "n2 Dirty mismatch") + assert.Equal(t, n2Deleted, true, "n2 Deleted mismatch") +} + +func TestLocalMigration9(t *testing.T) { + // set up + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-9-pre-schema.sql", SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + + b1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "b1") + + n1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting n1", db, `INSERT INTO notes + (uuid, book_uuid, body, added_on, edited_on, public, dirty, usn, deleted) VALUES + (?, ?, ?, ?, ?, ?, ?, ?, ?)`, n1UUID, b1UUID, "n1 Body", 1, 2, true, true, 20, false) + n2UUID := utils.GenerateUUID() + database.MustExec(t, "inserting n2", db, `INSERT INTO notes + (uuid, book_uuid, body, added_on, edited_on, public, dirty, usn, deleted) VALUES + (?, ?, ?, ?, ?, ?, ?, ?, ?)`, n2UUID, b1UUID, "n2 Body", 3, 4, false, true, 21, false) + + // Execute + tx, err := db.Begin() + if err != nil { + t.Fatal(errors.Wrap(err, "beginning a transaction")) + } + + err = lm9.run(ctx, tx) + if err != nil { + tx.Rollback() + t.Fatal(errors.Wrap(err, "failed to run")) + } + + tx.Commit() + + // Test + + // assert that note_fts was populated with correct values + var noteFtsCount int + database.MustScan(t, "counting note_fts", db.QueryRow("SELECT count(*) FROM note_fts;"), ¬eFtsCount) + assert.Equal(t, noteFtsCount, 2, "noteFtsCount mismatch") + + var resCount int + database.MustScan(t, "counting result", db.QueryRow("SELECT count(*) FROM note_fts WHERE note_fts MATCH ?", "n1"), &resCount) + assert.Equal(t, resCount, 1, "noteFtsCount mismatch") +} + +func TestLocalMigration10(t *testing.T) { + // set up + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-10-pre-schema.sql", SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + + b1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "123") + b2UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 2", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "123 javascript") + b3UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 3", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b3UUID, "foo") + b4UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 4", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b4UUID, "+123") + b5UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 5", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b5UUID, "0123") + b6UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 6", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b6UUID, "javascript 123") + b7UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 7", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b7UUID, "123 (1)") + b8UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 8", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b8UUID, "5") + + // Execute + tx, err := db.Begin() + if err != nil { + t.Fatal(errors.Wrap(err, "beginning a transaction")) + } + + err = lm10.run(ctx, tx) + if err != nil { + tx.Rollback() + t.Fatal(errors.Wrap(err, "failed to run")) + } + + tx.Commit() + + // Test + + // assert that note_fts was populated with correct values + var b1Label, b2Label, b3Label, b4Label, b5Label, b6Label, b7Label, b8Label string + var b1Dirty, b2Dirty, b3Dirty, b4Dirty, b5Dirty, b6Dirty, b7Dirty, b8Dirty bool + + database.MustScan(t, "getting b1", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty) + database.MustScan(t, "getting b2", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b2UUID), &b2Label, &b2Dirty) + database.MustScan(t, "getting b3", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b3UUID), &b3Label, &b3Dirty) + database.MustScan(t, "getting b4", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b4UUID), &b4Label, &b4Dirty) + database.MustScan(t, "getting b5", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b5UUID), &b5Label, &b5Dirty) + database.MustScan(t, "getting b6", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b6UUID), &b6Label, &b6Dirty) + database.MustScan(t, "getting b7", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b7UUID), &b7Label, &b7Dirty) + database.MustScan(t, "getting b8", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b8UUID), &b8Label, &b8Dirty) + + assert.Equal(t, b1Label, "123 (2)", "b1Label mismatch") + assert.Equal(t, b1Dirty, true, "b1Dirty mismatch") + assert.Equal(t, b2Label, "123 javascript", "b2Label mismatch") + assert.Equal(t, b2Dirty, false, "b2Dirty mismatch") + assert.Equal(t, b3Label, "foo", "b3Label mismatch") + assert.Equal(t, b3Dirty, false, "b3Dirty mismatch") + assert.Equal(t, b4Label, "+123", "b4Label mismatch") + assert.Equal(t, b4Dirty, false, "b4Dirty mismatch") + assert.Equal(t, b5Label, "0123 (1)", "b5Label mismatch") + assert.Equal(t, b5Dirty, true, "b5Dirty mismatch") + assert.Equal(t, b6Label, "javascript 123", "b6Label mismatch") + assert.Equal(t, b6Dirty, false, "b6Dirty mismatch") + assert.Equal(t, b7Label, "123 (1)", "b7Label mismatch") + assert.Equal(t, b7Dirty, false, "b7Dirty mismatch") + assert.Equal(t, b8Label, "5 (1)", "b8Label mismatch") + assert.Equal(t, b8Dirty, true, "b8Dirty mismatch") +} + +func TestLocalMigration11(t *testing.T) { + // set up + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/local-11-pre-schema.sql", SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + + db := ctx.DB + + b1UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "foo") + b2UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 2", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "bar baz") + b3UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 3", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b3UUID, "quz qux") + b4UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 4", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b4UUID, "quz_qux") + b5UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 5", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b5UUID, "foo bar baz quz 123") + b6UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 6", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b6UUID, "foo_bar baz") + b7UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 7", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b7UUID, "cool ideas") + b8UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 8", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b8UUID, "cool_ideas") + b9UUID := utils.GenerateUUID() + database.MustExec(t, "inserting book 9", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b9UUID, "cool_ideas_2") + + // Execute + tx, err := db.Begin() + if err != nil { + t.Fatal(errors.Wrap(err, "beginning a transaction")) + } + + err = lm11.run(ctx, tx) + if err != nil { + tx.Rollback() + t.Fatal(errors.Wrap(err, "failed to run")) + } + + tx.Commit() + + // Test + var bookCount int + database.MustScan(t, "counting books", db.QueryRow("SELECT count(*) FROM books"), &bookCount) + assert.Equal(t, bookCount, 9, "bookCount mismatch") + + // assert that note_fts was populated with correct values + var b1Label, b2Label, b3Label, b4Label, b5Label, b6Label, b7Label, b8Label, b9Label string + var b1Dirty, b2Dirty, b3Dirty, b4Dirty, b5Dirty, b6Dirty, b7Dirty, b8Dirty, b9Dirty bool + + database.MustScan(t, "getting b1", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b1UUID), &b1Label, &b1Dirty) + database.MustScan(t, "getting b2", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b2UUID), &b2Label, &b2Dirty) + database.MustScan(t, "getting b3", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b3UUID), &b3Label, &b3Dirty) + database.MustScan(t, "getting b4", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b4UUID), &b4Label, &b4Dirty) + database.MustScan(t, "getting b5", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b5UUID), &b5Label, &b5Dirty) + database.MustScan(t, "getting b6", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b6UUID), &b6Label, &b6Dirty) + database.MustScan(t, "getting b7", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b7UUID), &b7Label, &b7Dirty) + database.MustScan(t, "getting b8", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b8UUID), &b8Label, &b8Dirty) + database.MustScan(t, "getting b9", db.QueryRow("SELECT label, dirty FROM books WHERE uuid = ?", b9UUID), &b9Label, &b9Dirty) + + assert.Equal(t, b1Label, "foo", "b1Label mismatch") + assert.Equal(t, b1Dirty, false, "b1Dirty mismatch") + assert.Equal(t, b2Label, "bar_baz", "b2Label mismatch") + assert.Equal(t, b2Dirty, true, "b2Dirty mismatch") + assert.Equal(t, b3Label, "quz_qux_2", "b3Label mismatch") + assert.Equal(t, b3Dirty, true, "b3Dirty mismatch") + assert.Equal(t, b4Label, "quz_qux", "b4Label mismatch") + assert.Equal(t, b4Dirty, false, "b4Dirty mismatch") + assert.Equal(t, b5Label, "foo_bar_baz_quz_123", "b5Label mismatch") + assert.Equal(t, b5Dirty, true, "b5Dirty mismatch") + assert.Equal(t, b6Label, "foo_bar_baz", "b6Label mismatch") + assert.Equal(t, b6Dirty, true, "b6Dirty mismatch") + assert.Equal(t, b7Label, "cool_ideas_3", "b7Label mismatch") + assert.Equal(t, b7Dirty, true, "b7Dirty mismatch") + assert.Equal(t, b8Label, "cool_ideas", "b8Label mismatch") + assert.Equal(t, b8Dirty, false, "b8Dirty mismatch") + assert.Equal(t, b9Label, "cool_ideas_2", "b9Label mismatch") + assert.Equal(t, b9Dirty, false, "b9Dirty mismatch") +} + +func TestRemoteMigration1(t *testing.T) { + // set up + opts := database.TestDBOptions{SchemaSQLPath: "./fixtures/remote-1-pre-schema.sql", SkipMigration: true} + ctx := context.InitTestCtx(t, "../tmp", &opts) + defer context.TeardownTestCtx(t, ctx) + testutils.Login(t, &ctx) + + JSBookUUID := "existing-js-book-uuid" + CSSBookUUID := "existing-css-book-uuid" + linuxBookUUID := "existing-linux-book-uuid" + newJSBookUUID := "new-js-book-uuid" + newCSSBookUUID := "new-css-book-uuid" + + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.String() == "/v1/books" { + res := []struct { + UUID string `json:"uuid"` + Label string `json:"label"` + }{ + { + UUID: newJSBookUUID, + Label: "js", + }, + { + UUID: newCSSBookUUID, + Label: "css", + }, + // book that only exists on the server. client must ignore. + { + UUID: "golang-book-uuid", + Label: "golang", + }, + } + + if err := json.NewEncoder(w).Encode(res); err != nil { + t.Fatal(errors.Wrap(err, "encoding response")) + } + } + })) + defer server.Close() + + ctx.APIEndpoint = server.URL + + db := ctx.DB + database.MustExec(t, "inserting js book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", JSBookUUID, "js") + database.MustExec(t, "inserting css book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", CSSBookUUID, "css") + database.MustExec(t, "inserting linux book", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", linuxBookUUID, "linux") + database.MustExec(t, "inserting sessionKey", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemSessionKey, "someSessionKey") + database.MustExec(t, "inserting sessionKeyExpiry", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemSessionKeyExpiry, time.Now().Add(24*time.Hour).Unix()) + + tx, err := db.Begin() + if err != nil { + t.Fatal(errors.Wrap(err, "beginning a transaction")) + } + + err = rm1.run(ctx, tx) + if err != nil { + tx.Rollback() + t.Fatal(errors.Wrap(err, "failed to run")) + } + + tx.Commit() + + // test + var postJSBookUUID, postCSSBookUUID, postLinuxBookUUID string + database.MustScan(t, "getting js book uuid", db.QueryRow("SELECT uuid FROM books WHERE label = ?", "js"), &postJSBookUUID) + database.MustScan(t, "getting css book uuid", db.QueryRow("SELECT uuid FROM books WHERE label = ?", "css"), &postCSSBookUUID) + database.MustScan(t, "getting linux book uuid", db.QueryRow("SELECT uuid FROM books WHERE label = ?", "linux"), &postLinuxBookUUID) + + assert.Equal(t, postJSBookUUID, newJSBookUUID, "js book uuid was not updated correctly") + assert.Equal(t, postCSSBookUUID, newCSSBookUUID, "css book uuid was not updated correctly") + assert.Equal(t, postLinuxBookUUID, linuxBookUUID, "linux book uuid changed") +} diff --git a/cli/migrate/migrations.go b/pkg/cli/migrate/migrations.go similarity index 94% rename from cli/migrate/migrations.go rename to pkg/cli/migrate/migrations.go index 3f6ad00d..e4392b00 100644 --- a/cli/migrate/migrations.go +++ b/pkg/cli/migrate/migrations.go @@ -26,20 +26,21 @@ import ( "strings" "github.com/dnote/actions" - "github.com/dnote/dnote/cli/client" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" + "github.com/dnote/dnote/pkg/cli/client" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/log" "github.com/pkg/errors" ) type migration struct { name string - run func(ctx infra.DnoteCtx, tx *infra.DB) error + run func(ctx context.DnoteCtx, tx *database.DB) error } var lm1 = migration{ name: "upgrade-edit-note-from-v1-to-v3", - run: func(ctx infra.DnoteCtx, tx *infra.DB) error { + run: func(ctx context.DnoteCtx, tx *database.DB) error { rows, err := tx.Query("SELECT uuid, data FROM actions WHERE type = ? AND schema = ?", "edit_note", 1) if err != nil { return errors.Wrap(err, "querying rows") @@ -87,7 +88,7 @@ var lm1 = migration{ var lm2 = migration{ name: "upgrade-edit-note-from-v2-to-v3", - run: func(ctx infra.DnoteCtx, tx *infra.DB) error { + run: func(ctx context.DnoteCtx, tx *database.DB) error { rows, err := tx.Query("SELECT uuid, data FROM actions WHERE type = ? AND schema = ?", "edit_note", 2) if err != nil { return errors.Wrap(err, "querying rows") @@ -132,7 +133,7 @@ var lm2 = migration{ var lm3 = migration{ name: "upgrade-remove-note-from-v1-to-v2", - run: func(ctx infra.DnoteCtx, tx *infra.DB) error { + run: func(ctx context.DnoteCtx, tx *database.DB) error { rows, err := tx.Query("SELECT uuid, data FROM actions WHERE type = ? AND schema = ?", "remove_note", 1) if err != nil { return errors.Wrap(err, "querying rows") @@ -174,7 +175,7 @@ var lm3 = migration{ var lm4 = migration{ name: "add-dirty-usn-deleted-to-notes-and-books", - run: func(ctx infra.DnoteCtx, tx *infra.DB) error { + run: func(ctx context.DnoteCtx, tx *database.DB) error { _, err := tx.Exec("ALTER TABLE books ADD COLUMN dirty bool DEFAULT false") if err != nil { return errors.Wrap(err, "adding dirty column to books") @@ -211,7 +212,7 @@ var lm4 = migration{ var lm5 = migration{ name: "mark-action-targets-dirty", - run: func(ctx infra.DnoteCtx, tx *infra.DB) error { + run: func(ctx context.DnoteCtx, tx *database.DB) error { rows, err := tx.Query("SELECT uuid, data, type FROM actions") if err != nil { return errors.Wrap(err, "querying rows") @@ -273,7 +274,7 @@ var lm5 = migration{ var lm6 = migration{ name: "drop-actions", - run: func(ctx infra.DnoteCtx, tx *infra.DB) error { + run: func(ctx context.DnoteCtx, tx *database.DB) error { _, err := tx.Exec("DROP TABLE actions;") if err != nil { return errors.Wrap(err, "dropping the actions table") @@ -285,7 +286,7 @@ var lm6 = migration{ var lm7 = migration{ name: "resolve-conflicts-with-reserved-book-names", - run: func(ctx infra.DnoteCtx, tx *infra.DB) error { + run: func(ctx context.DnoteCtx, tx *database.DB) error { migrateBook := func(name string) error { var uuid string @@ -332,7 +333,7 @@ var lm7 = migration{ var lm8 = migration{ name: "drop-note-id-and-rename-content-to-body", - run: func(ctx infra.DnoteCtx, tx *infra.DB) error { + run: func(ctx context.DnoteCtx, tx *database.DB) error { _, err := tx.Exec(`CREATE TABLE notes_tmp ( uuid text NOT NULL, @@ -371,7 +372,7 @@ var lm8 = migration{ var lm9 = migration{ name: "create-fts-index", - run: func(ctx infra.DnoteCtx, tx *infra.DB) error { + run: func(ctx context.DnoteCtx, tx *database.DB) error { _, err := tx.Exec(`CREATE VIRTUAL TABLE IF NOT EXISTS note_fts USING fts5(content=notes, body, tokenize="porter unicode61 categories 'L* N* Co Ps Pe'");`) if err != nil { return errors.Wrap(err, "creating note_fts") @@ -407,7 +408,7 @@ var lm9 = migration{ var lm10 = migration{ name: "rename-number-only-book", - run: func(ctx infra.DnoteCtx, tx *infra.DB) error { + run: func(ctx context.DnoteCtx, tx *database.DB) error { migrateBook := func(label string) error { var uuid string @@ -467,7 +468,7 @@ var lm10 = migration{ var lm11 = migration{ name: "rename-book-labels-with-space", - run: func(ctx infra.DnoteCtx, tx *infra.DB) error { + run: func(ctx context.DnoteCtx, tx *database.DB) error { processLabel := func(label string) (string, error) { sanitized := strings.Replace(label, " ", "_", -1) @@ -531,7 +532,7 @@ var lm11 = migration{ var rm1 = migration{ name: "sync-book-uuids-from-server", - run: func(ctx infra.DnoteCtx, tx *infra.DB) error { + run: func(ctx context.DnoteCtx, tx *database.DB) error { sessionKey := ctx.SessionKey if sessionKey == "" { return errors.New("not logged in") diff --git a/cli/core/output.go b/pkg/cli/output/output.go similarity index 82% rename from cli/core/output.go rename to pkg/cli/output/output.go index 092a0218..d2248523 100644 --- a/cli/core/output.go +++ b/pkg/cli/output/output.go @@ -16,17 +16,20 @@ * along with Dnote CLI. If not, see . */ -package core +// Package output provides functions to print informations on the terminal +// in a consistent manner +package output import ( "fmt" "time" - "github.com/dnote/dnote/cli/log" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/log" ) -// PrintNoteInfo prints a note information -func PrintNoteInfo(info NoteInfo) { +// NoteInfo prints a note information +func NoteInfo(info database.NoteInfo) { log.Infof("book name: %s\n", info.BookLabel) log.Infof("created at: %s\n", time.Unix(0, info.AddedOn).Format("Jan 2, 2006 3:04pm (MST)")) if info.EditedOn != 0 { diff --git a/cli/scripts/build.sh b/pkg/cli/scripts/build.sh similarity index 97% rename from cli/scripts/build.sh rename to pkg/cli/scripts/build.sh index bb2423af..6fdbb2f1 100755 --- a/cli/scripts/build.sh +++ b/pkg/cli/scripts/build.sh @@ -9,7 +9,7 @@ set -eu version="$1" projectDir="$GOPATH/src/github.com/dnote/dnote" -basedir="$GOPATH/src/github.com/dnote/dnote/cli" +basedir="$GOPATH/src/github.com/dnote/dnote/pkg/cli" TMP="$basedir/build" command_exists () { diff --git a/cli/scripts/dev.sh b/pkg/cli/scripts/dev.sh similarity index 81% rename from cli/scripts/dev.sh rename to pkg/cli/scripts/dev.sh index 96bd040b..ebe2419d 100755 --- a/cli/scripts/dev.sh +++ b/pkg/cli/scripts/dev.sh @@ -6,6 +6,6 @@ set -eux sudo rm -rf "$(which dnote)" "$GOPATH/bin/cli" # change tags to darwin if on macos -go install -ldflags "-X main.apiEndpoint=http://127.0.0.1:5000" --tags "linux fts5" "$GOPATH/src/github.com/dnote/dnote/cli/." +go install -ldflags "-X main.apiEndpoint=http://127.0.0.1:5000" --tags "linux fts5" "$GOPATH/src/github.com/dnote/dnote/pkg/cli/." sudo ln -s "$GOPATH/bin/cli" /usr/local/bin/dnote diff --git a/cli/scripts/dump_schema.sh b/pkg/cli/scripts/dump_schema.sh similarity index 100% rename from cli/scripts/dump_schema.sh rename to pkg/cli/scripts/dump_schema.sh diff --git a/cli/scripts/release.sh b/pkg/cli/scripts/release.sh similarity index 100% rename from cli/scripts/release.sh rename to pkg/cli/scripts/release.sh diff --git a/cli/scripts/test.sh b/pkg/cli/scripts/test.sh similarity index 85% rename from cli/scripts/test.sh rename to pkg/cli/scripts/test.sh index 1af25c1b..9320733b 100755 --- a/cli/scripts/test.sh +++ b/pkg/cli/scripts/test.sh @@ -4,7 +4,7 @@ set -eux -basePath="$GOPATH/src/github.com/dnote/dnote/cli" +basePath="$GOPATH/src/github.com/dnote/dnote/pkg/cli" # clear tmp dir in case not properly torn down rm -rf "$basePath/tmp" diff --git a/pkg/cli/testutils/main.go b/pkg/cli/testutils/main.go new file mode 100644 index 00000000..5bf9f4c1 --- /dev/null +++ b/pkg/cli/testutils/main.go @@ -0,0 +1,228 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +// Package testutils provides utilities used in tests +package testutils + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/dnote/dnote/pkg/cli/consts" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/database" + "github.com/dnote/dnote/pkg/cli/utils" + "github.com/pkg/errors" +) + +// Login simulates a logged in user by inserting credentials in the local database +func Login(t *testing.T, ctx *context.DnoteCtx) { + db := ctx.DB + + database.MustExec(t, "inserting sessionKey", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemSessionKey, "someSessionKey") + database.MustExec(t, "inserting sessionKeyExpiry", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemSessionKeyExpiry, time.Now().Add(24*time.Hour).Unix()) + database.MustExec(t, "inserting cipherKey", db, "INSERT INTO system (key, value) VALUES (?, ?)", consts.SystemCipherKey, "QUVTMjU2S2V5LTMyQ2hhcmFjdGVyczEyMzQ1Njc4OTA=") + + ctx.SessionKey = "someSessionKey" + ctx.SessionKeyExpiry = time.Now().Add(24 * time.Hour).Unix() + ctx.CipherKey = []byte("AES256Key-32Characters1234567890") +} + +// RemoveDir cleans up the test env represented by the given context +func RemoveDir(t *testing.T, dir string) { + if err := os.RemoveAll(dir); err != nil { + t.Fatal(errors.Wrap(err, "removing the directory")) + } +} + +// CopyFixture writes the content of the given fixture to the filename inside the dnote dir +func CopyFixture(t *testing.T, ctx context.DnoteCtx, fixturePath string, filename string) { + fp, err := filepath.Abs(fixturePath) + if err != nil { + t.Fatal(errors.Wrap(err, "getting the absolute path for fixture")) + } + + dp, err := filepath.Abs(filepath.Join(ctx.DnoteDir, filename)) + if err != nil { + t.Fatal(errors.Wrap(err, "getting the absolute path dnote dir")) + } + + err = utils.CopyFile(fp, dp) + if err != nil { + t.Fatal(errors.Wrap(err, "copying the file")) + } +} + +// WriteFile writes a file with the given content and filename inside the dnote dir +func WriteFile(ctx context.DnoteCtx, content []byte, filename string) { + dp, err := filepath.Abs(filepath.Join(ctx.DnoteDir, filename)) + if err != nil { + panic(err) + } + + if err := ioutil.WriteFile(dp, content, 0644); err != nil { + panic(err) + } +} + +// ReadFile reads the content of the file with the given name in dnote dir +func ReadFile(ctx context.DnoteCtx, filename string) []byte { + path := filepath.Join(ctx.DnoteDir, filename) + + b, err := ioutil.ReadFile(path) + if err != nil { + panic(err) + } + + return b +} + +// ReadJSON reads JSON fixture to the struct at the destination address +func ReadJSON(path string, destination interface{}) { + var dat []byte + dat, err := ioutil.ReadFile(path) + if err != nil { + panic(errors.Wrap(err, "Failed to load fixture payload")) + } + if err := json.Unmarshal(dat, destination); err != nil { + panic(errors.Wrap(err, "Failed to get event")) + } +} + +// NewDnoteCmd returns a new Dnote command and a pointer to stderr +func NewDnoteCmd(opts RunDnoteCmdOptions, binaryName string, arg ...string) (*exec.Cmd, *bytes.Buffer, *bytes.Buffer, error) { + var stderr, stdout bytes.Buffer + + binaryPath, err := filepath.Abs(binaryName) + if err != nil { + return &exec.Cmd{}, &stderr, &stdout, errors.Wrap(err, "getting the absolute path to the test binary") + } + + cmd := exec.Command(binaryPath, arg...) + cmd.Env = []string{fmt.Sprintf("DNOTE_DIR=%s", opts.DnoteDir), fmt.Sprintf("DNOTE_HOME_DIR=%s", opts.HomeDir)} + cmd.Stderr = &stderr + cmd.Stdout = &stdout + + return cmd, &stderr, &stdout, nil +} + +// RunDnoteCmdOptions is an option for RunDnoteCmd +type RunDnoteCmdOptions struct { + DnoteDir string + HomeDir string +} + +// RunDnoteCmd runs a dnote command +func RunDnoteCmd(t *testing.T, opts RunDnoteCmdOptions, binaryName string, arg ...string) { + t.Logf("running: %s %s", binaryName, strings.Join(arg, " ")) + + cmd, stderr, stdout, err := NewDnoteCmd(opts, binaryName, arg...) + if err != nil { + t.Logf("\n%s", stdout) + t.Fatal(errors.Wrap(err, "getting command").Error()) + } + + cmd.Env = append(cmd.Env, "DNOTE_DEBUG=1") + + if err := cmd.Run(); err != nil { + t.Logf("\n%s", stdout) + t.Fatal(errors.Wrapf(err, "running command %s", stderr.String())) + } + + // Print stdout if and only if test fails later + t.Logf("\n%s", stdout) +} + +// WaitDnoteCmd runs a dnote command and waits until the command is exited +func WaitDnoteCmd(t *testing.T, opts RunDnoteCmdOptions, runFunc func(io.WriteCloser) error, binaryName string, arg ...string) { + t.Logf("running: %s %s", binaryName, strings.Join(arg, " ")) + + cmd, stderr, stdout, err := NewDnoteCmd(opts, binaryName, arg...) + if err != nil { + t.Logf("\n%s", stdout) + t.Fatal(errors.Wrap(err, "getting command").Error()) + } + + stdin, err := cmd.StdinPipe() + if err != nil { + t.Logf("\n%s", stdout) + t.Fatal(errors.Wrap(err, "getting stdin %s")) + } + defer stdin.Close() + + // Start the program + err = cmd.Start() + if err != nil { + t.Logf("\n%s", stdout) + t.Fatal(errors.Wrap(err, "starting command")) + } + + err = runFunc(stdin) + if err != nil { + t.Logf("\n%s", stdout) + t.Fatal(errors.Wrap(err, "running with stdin")) + } + + err = cmd.Wait() + if err != nil { + t.Logf("\n%s", stdout) + t.Fatal(errors.Wrapf(err, "running command %s", stderr.String())) + } + + // Print stdout if and only if test fails later + t.Logf("\n%s", stdout) +} + +// UserConfirm simulates confirmation from the user by writing to stdin +func UserConfirm(stdin io.WriteCloser) error { + // confirm + if _, err := io.WriteString(stdin, "y\n"); err != nil { + return errors.Wrap(err, "indicating confirmation in stdin") + } + + return nil +} + +// MustMarshalJSON marshalls the given interface into JSON. +// If there is any error, it fails the test. +func MustMarshalJSON(t *testing.T, v interface{}) []byte { + b, err := json.Marshal(v) + if err != nil { + t.Fatalf("%s: marshalling data", t.Name()) + } + + return b +} + +// MustUnmarshalJSON marshalls the given interface into JSON. +// If there is any error, it fails the test. +func MustUnmarshalJSON(t *testing.T, data []byte, v interface{}) { + err := json.Unmarshal(data, v) + if err != nil { + t.Fatalf("%s: unmarshalling data", t.Name()) + } +} diff --git a/pkg/cli/testutils/setup.go b/pkg/cli/testutils/setup.go new file mode 100644 index 00000000..6b62ecc0 --- /dev/null +++ b/pkg/cli/testutils/setup.go @@ -0,0 +1,71 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +package testutils + +import ( + "testing" + + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/database" +) + +// Setup1 sets up a dnote env #1 +func Setup1(t *testing.T, ctx context.DnoteCtx) { + db := ctx.DB + + b1UUID := "js-book-uuid" + b2UUID := "linux-book-uuid" + + database.MustExec(t, "setting up book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "js") + database.MustExec(t, "setting up book 2", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b2UUID, "linux") + + database.MustExec(t, "setting up note 1", db, "INSERT INTO notes (uuid, book_uuid, body, added_on) VALUES (?, ?, ?, ?)", "43827b9a-c2b0-4c06-a290-97991c896653", b1UUID, "Booleans have toString()", 1515199943) +} + +// Setup2 sets up a dnote env #2 +func Setup2(t *testing.T, db *database.DB) { + b1UUID := "js-book-uuid" + b2UUID := "linux-book-uuid" + + database.MustExec(t, "setting up book 1", db, "INSERT INTO books (uuid, label, usn) VALUES (?, ?, ?)", b1UUID, "js", 111) + database.MustExec(t, "setting up book 2", db, "INSERT INTO books (uuid, label, usn) VALUES (?, ?, ?)", b2UUID, "linux", 122) + + database.MustExec(t, "setting up note 1", db, "INSERT INTO notes (uuid, book_uuid, body, added_on, usn) VALUES (?, ?, ?, ?, ?)", "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", b1UUID, "n1 body", 1515199951, 11) + database.MustExec(t, "setting up note 2", db, "INSERT INTO notes (uuid, book_uuid, body, added_on, usn) VALUES (?, ?, ?, ?, ?)", "43827b9a-c2b0-4c06-a290-97991c896653", b1UUID, "n2 body", 1515199943, 12) + database.MustExec(t, "setting up note 3", db, "INSERT INTO notes (uuid, book_uuid, body, added_on, usn) VALUES (?, ?, ?, ?, ?)", "3e065d55-6d47-42f2-a6bf-f5844130b2d2", b2UUID, "n3 body", 1515199961, 13) +} + +// Setup3 sets up a dnote env #3 +func Setup3(t *testing.T, db *database.DB) { + b1UUID := "js-book-uuid" + + database.MustExec(t, "setting up book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "js") + + database.MustExec(t, "setting up note 1", db, "INSERT INTO notes (uuid, book_uuid, body, added_on) VALUES (?, ?, ?, ?)", "43827b9a-c2b0-4c06-a290-97991c896653", b1UUID, "Booleans have toString()", 1515199943) +} + +// Setup4 sets up a dnote env #4 +func Setup4(t *testing.T, db *database.DB) { + b1UUID := "js-book-uuid" + + database.MustExec(t, "setting up book 1", db, "INSERT INTO books (uuid, label) VALUES (?, ?)", b1UUID, "js") + + database.MustExec(t, "setting up note 1", db, "INSERT INTO notes (rowid, uuid, book_uuid, body, added_on) VALUES (?, ?, ?, ?, ?)", 1, "43827b9a-c2b0-4c06-a290-97991c896653", b1UUID, "Booleans have toString()", 1515199943) + database.MustExec(t, "setting up note 2", db, "INSERT INTO notes (rowid, uuid, book_uuid, body, added_on) VALUES (?, ?, ?, ?, ?)", 2, "f0d0fbb7-31ff-45ae-9f0f-4e429c0c797f", b1UUID, "Date object implements mathematical comparisons", 1515199951) +} diff --git a/pkg/cli/ui/editor.go b/pkg/cli/ui/editor.go new file mode 100644 index 00000000..5ebdb195 --- /dev/null +++ b/pkg/cli/ui/editor.go @@ -0,0 +1,146 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +// Package ui provides the user interface for the program +package ui + +import ( + "fmt" + "io/ioutil" + "os" + "os/exec" + "strings" + + "github.com/dnote/dnote/pkg/cli/consts" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/infra" + "github.com/dnote/dnote/pkg/cli/utils" + "github.com/pkg/errors" +) + +// GetTmpContentPath returns the path to the temporary file containing +// content being added or edited +func GetTmpContentPath(ctx context.DnoteCtx) string { + return fmt.Sprintf("%s/%s", ctx.DnoteDir, consts.TmpContentFilename) +} + +// getEditorCommand returns the system's editor command with appropriate flags, +// if necessary, to make the command wait until editor is close to exit. +func getEditorCommand() string { + editor := os.Getenv("EDITOR") + + var ret string + + switch editor { + case "atom": + ret = "atom -w" + case "subl": + ret = "subl -n -w" + case "mate": + ret = "mate -w" + case "vim": + ret = "vim" + case "nano": + ret = "nano" + case "emacs": + ret = "emacs" + case "nvim": + ret = "nvim" + default: + ret = "vi" + } + + return ret +} + +// SanitizeContent sanitizes note content +func SanitizeContent(s string) string { + var ret string + + ret = strings.Trim(s, " ") + + // Remove newline at the end of the file because POSIX defines a line as + // characters followed by a newline + ret = strings.TrimSuffix(ret, "\n") + ret = strings.TrimSuffix(ret, "\r\n") + + return ret +} + +func newEditorCmd(ctx context.DnoteCtx, fpath string) (*exec.Cmd, error) { + config, err := infra.ReadConfig(ctx) + if err != nil { + return nil, errors.Wrap(err, "reading config") + } + + args := strings.Fields(config.Editor) + args = append(args, fpath) + + return exec.Command(args[0], args[1:]...), nil +} + +// GetEditorInput gets the user input by launching a text editor and waiting for +// it to exit +func GetEditorInput(ctx context.DnoteCtx, fpath string, content *string) error { + if !utils.FileExists(fpath) { + f, err := os.Create(fpath) + if err != nil { + return errors.Wrap(err, "creating a temporary content file") + } + err = f.Close() + if err != nil { + return errors.Wrap(err, "closing the temporary content file") + } + } + + cmd, err := newEditorCmd(ctx, fpath) + if err != nil { + return errors.Wrap(err, "creating an editor command") + } + + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + err = cmd.Start() + if err != nil { + return errors.Wrapf(err, "launching an editor") + } + + err = cmd.Wait() + if err != nil { + return errors.Wrap(err, "waiting for the editor") + } + + b, err := ioutil.ReadFile(fpath) + if err != nil { + return errors.Wrap(err, "reading the temporary content file") + } + + err = os.Remove(fpath) + if err != nil { + return errors.Wrap(err, "removing the temporary content file") + } + + raw := string(b) + c := SanitizeContent(raw) + + *content = c + + return nil +} diff --git a/pkg/cli/ui/terminal.go b/pkg/cli/ui/terminal.go new file mode 100644 index 00000000..87b1e486 --- /dev/null +++ b/pkg/cli/ui/terminal.go @@ -0,0 +1,97 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +package ui + +import ( + "bufio" + "fmt" + "os" + "strings" + "syscall" + + "github.com/dnote/dnote/pkg/cli/log" + "github.com/pkg/errors" + "golang.org/x/crypto/ssh/terminal" +) + +func readInput() (string, error) { + reader := bufio.NewReader(os.Stdin) + input, err := reader.ReadString('\n') + if err != nil { + return "", errors.Wrap(err, "reading stdin") + } + + return strings.Trim(input, "\r\n"), nil +} + +// PromptInput prompts the user input and saves the result to the destination +func PromptInput(message string, dest *string) error { + log.Askf(message, false) + + input, err := readInput() + if err != nil { + return errors.Wrap(err, "getting user input") + } + + *dest = input + + return nil +} + +// PromptPassword prompts the user input a password and saves the result to the destination. +// The input is masked, meaning it is not echoed on the terminal. +func PromptPassword(message string, dest *string) error { + log.Askf(message, true) + + password, err := terminal.ReadPassword(int(syscall.Stdin)) + if err != nil { + return errors.Wrap(err, "getting user input") + } + + fmt.Println("") + + *dest = string(password) + + return nil +} + +// Confirm prompts for user input to confirm a choice +func Confirm(question string, optimistic bool) (bool, error) { + var choices string + if optimistic { + choices = "(Y/n)" + } else { + choices = "(y/N)" + } + + message := fmt.Sprintf("%s %s", question, choices) + + var input string + if err := PromptInput(message, &input); err != nil { + return false, errors.Wrap(err, "Failed to get user input") + } + + confirmed := input == "y" + + if optimistic { + confirmed = confirmed || input == "" + } + + return confirmed, nil +} diff --git a/cli/core/upgrade.go b/pkg/cli/upgrade/upgrade.go similarity index 71% rename from cli/core/upgrade.go rename to pkg/cli/upgrade/upgrade.go index de3a3bab..d9b3844c 100644 --- a/cli/core/upgrade.go +++ b/pkg/cli/upgrade/upgrade.go @@ -16,16 +16,17 @@ * along with Dnote CLI. If not, see . */ -package core +package upgrade import ( - "context" + stdCtx "context" "fmt" "time" - "github.com/dnote/dnote/cli/infra" - "github.com/dnote/dnote/cli/log" - "github.com/dnote/dnote/cli/utils" + "github.com/dnote/dnote/pkg/cli/consts" + "github.com/dnote/dnote/pkg/cli/context" + "github.com/dnote/dnote/pkg/cli/log" + "github.com/dnote/dnote/pkg/cli/ui" "github.com/google/go-github/github" "github.com/pkg/errors" ) @@ -34,11 +35,11 @@ import ( var upgradeInterval int64 = 86400 * 7 * 3 // shouldCheckUpdate checks if update should be checked -func shouldCheckUpdate(ctx infra.DnoteCtx) (bool, error) { +func shouldCheckUpdate(ctx context.DnoteCtx) (bool, error) { db := ctx.DB var lastUpgrade int64 - err := db.QueryRow("SELECT value FROM system WHERE key = ?", infra.SystemLastUpgrade).Scan(&lastUpgrade) + err := db.QueryRow("SELECT value FROM system WHERE key = ?", consts.SystemLastUpgrade).Scan(&lastUpgrade) if err != nil { return false, errors.Wrap(err, "getting last_udpate") } @@ -48,11 +49,11 @@ func shouldCheckUpdate(ctx infra.DnoteCtx) (bool, error) { return now-lastUpgrade > upgradeInterval, nil } -func touchLastUpgrade(ctx infra.DnoteCtx) error { +func touchLastUpgrade(ctx context.DnoteCtx) error { db := ctx.DB now := time.Now().Unix() - _, err := db.Exec("UPDATE system SET value = ? WHERE key = ?", now, infra.SystemLastUpgrade) + _, err := db.Exec("UPDATE system SET value = ? WHERE key = ?", now, consts.SystemLastUpgrade) if err != nil { return errors.Wrap(err, "updating last_upgrade") } @@ -60,32 +61,34 @@ func touchLastUpgrade(ctx infra.DnoteCtx) error { return nil } -func checkVersion(ctx infra.DnoteCtx) error { +func checkVersion(ctx context.DnoteCtx) error { log.Infof("current version is %s\n", ctx.Version) // Fetch the latest version gh := github.NewClient(nil) - releases, _, err := gh.Repositories.ListReleases(context.Background(), "dnote", "cli", nil) + releases, _, err := gh.Repositories.ListReleases(stdCtx.Background(), "dnote", "cli", nil) if err != nil { return errors.Wrap(err, "fetching releases") } latest := releases[0] - latestVersion := (*latest.TagName)[1:] + + // releases are tagged in a form of cli-v1.0.0 + latestVersion := (*latest.TagName)[5:] log.Infof("latest version is %s\n", latestVersion) if latestVersion == ctx.Version { log.Success("you are up-to-date\n\n") } else { - log.Infof("to upgrade, see https://github.com/dnote/dnote/cli/blob/master/README.md\n") + log.Infof("to upgrade, see https://github.com/dnote/dnote/pkg/cli/blob/master/README.md\n") } return nil } -// CheckUpdate triggers update if needed -func CheckUpdate(ctx infra.DnoteCtx) error { +// Check triggers update if needed +func Check(ctx context.DnoteCtx) error { shouldCheck, err := shouldCheckUpdate(ctx) if err != nil { return errors.Wrap(err, "checking if dnote should check update") @@ -100,7 +103,7 @@ func CheckUpdate(ctx infra.DnoteCtx) error { } fmt.Printf("\n") - willCheck, err := utils.AskConfirmation("check for upgrade?", true) + willCheck, err := ui.Confirm("check for upgrade?", true) if err != nil { return errors.Wrap(err, "getting user confirmation") } diff --git a/cli/utils/diff/diff.go b/pkg/cli/utils/diff/diff.go similarity index 100% rename from cli/utils/diff/diff.go rename to pkg/cli/utils/diff/diff.go diff --git a/cli/utils/diff/diff_test.go b/pkg/cli/utils/diff/diff_test.go similarity index 95% rename from cli/utils/diff/diff_test.go rename to pkg/cli/utils/diff/diff_test.go index fca05da4..f784b1e2 100644 --- a/cli/utils/diff/diff_test.go +++ b/pkg/cli/utils/diff/diff_test.go @@ -22,7 +22,7 @@ import ( "fmt" "testing" - "github.com/dnote/dnote/cli/testutils" + "github.com/dnote/dnote/pkg/assert" "github.com/sergi/go-diff/diffmatchpatch" ) @@ -143,7 +143,7 @@ func TestDo(t *testing.T) { result := Do(tc.s1, tc.s2) t.Run(fmt.Sprintf("test case %d", idx), func(t *testing.T) { - testutils.AssertDeepEqual(t, result, tc.expected, "result mismatch") + assert.DeepEqual(t, result, tc.expected, "result mismatch") }) } } diff --git a/cli/utils/utils.go b/pkg/cli/utils/files.go similarity index 55% rename from cli/utils/utils.go rename to pkg/cli/utils/files.go index 9a4d4a10..452ee475 100644 --- a/cli/utils/utils.go +++ b/pkg/cli/utils/files.go @@ -19,92 +19,28 @@ package utils import ( - "bufio" - "fmt" "io" "io/ioutil" - "net/http" "os" "path/filepath" - "regexp" - "strings" - "syscall" - "github.com/dnote/dnote/cli/log" "github.com/pkg/errors" - "github.com/satori/go.uuid" - "golang.org/x/crypto/ssh/terminal" ) -// GenerateUUID returns a uid -func GenerateUUID() string { - return uuid.NewV4().String() -} - -func getInput() (string, error) { - reader := bufio.NewReader(os.Stdin) - input, err := reader.ReadString('\n') +// ReadFileAbs reads the content of the file with the given file path by resolving +// it as an absolute path +func ReadFileAbs(relpath string) []byte { + fp, err := filepath.Abs(relpath) if err != nil { - return "", errors.Wrap(err, "reading stdin") + panic(err) } - return strings.Trim(input, "\r\n"), nil -} - -// PromptInput prompts the user input and saves the result to the destination -func PromptInput(message string, dest *string) error { - log.Askf(message, false) - - input, err := getInput() + b, err := ioutil.ReadFile(fp) if err != nil { - return errors.Wrap(err, "getting user input") + panic(err) } - *dest = input - - return nil -} - -// PromptPassword prompts the user input a password and saves the result to the destination. -// The input is masked, meaning it is not echoed on the terminal. -func PromptPassword(message string, dest *string) error { - log.Askf(message, true) - - password, err := terminal.ReadPassword(int(syscall.Stdin)) - if err != nil { - return errors.Wrap(err, "getting user input") - } - - fmt.Println("") - - *dest = string(password) - - return nil -} - -// AskConfirmation prompts for user input to confirm a choice -func AskConfirmation(question string, optimistic bool) (bool, error) { - var choices string - if optimistic { - choices = "(Y/n)" - } else { - choices = "(y/N)" - } - - message := fmt.Sprintf("%s %s", question, choices) - - var input string - if err := PromptInput(message, &input); err != nil { - return false, errors.Wrap(err, "Failed to get user input") - } - - confirmed := input == "y" - - if optimistic { - confirmed = confirmed || input == "" - } - - return confirmed, nil + return b } // FileExists checks if the file exists at the given path @@ -160,22 +96,6 @@ func CopyDir(src, dest string) error { return nil } -// checkRespErr checks if the given http response indicates an error. It returns a boolean indicating -// if the response is an error, and a decoded error message. -func checkRespErr(res *http.Response) error { - if res.StatusCode < 400 { - return nil - } - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - return errors.Wrapf(err, "server responded with %d but could not read the response body", res.StatusCode) - } - - bodyStr := string(body) - return errors.Errorf(`response %d "%s"`, res.StatusCode, strings.TrimRight(bodyStr, "\n")) -} - // CopyFile copies a file from the src to dest func CopyFile(src, dest string) error { in, err := os.Open(src) @@ -213,15 +133,3 @@ func CopyFile(src, dest string) error { return nil } - -// regexNumber is a regex that matches a string that looks like an integer -var regexNumber = regexp.MustCompile(`^\d+$`) - -// IsNumber checks if the given string is in the form of a number -func IsNumber(s string) bool { - if s == "" { - return false - } - - return regexNumber.MatchString(s) -} diff --git a/pkg/cli/utils/utils.go b/pkg/cli/utils/utils.go new file mode 100644 index 00000000..0321c829 --- /dev/null +++ b/pkg/cli/utils/utils.go @@ -0,0 +1,42 @@ +/* Copyright (C) 2019 Monomax Software Pty Ltd + * + * This file is part of Dnote CLI. + * + * Dnote CLI 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 CLI 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 CLI. If not, see . + */ + +package utils + +import ( + "regexp" + + "github.com/satori/go.uuid" +) + +// GenerateUUID returns a uid +func GenerateUUID() string { + return uuid.NewV4().String() +} + +// regexNumber is a regex that matches a string that looks like an integer +var regexNumber = regexp.MustCompile(`^\d+$`) + +// IsNumber checks if the given string is in the form of a number +func IsNumber(s string) bool { + if s == "" { + return false + } + + return regexNumber.MatchString(s) +} diff --git a/server/.gitignore b/pkg/server/.gitignore similarity index 100% rename from server/.gitignore rename to pkg/server/.gitignore diff --git a/server/Gopkg.lock b/pkg/server/Gopkg.lock similarity index 95% rename from server/Gopkg.lock rename to pkg/server/Gopkg.lock index 12db3dda..7c80517d 100644 --- a/server/Gopkg.lock +++ b/pkg/server/Gopkg.lock @@ -29,6 +29,14 @@ revision = "c5c95ec357c8235fbd7f34e8c843d36783f3fad9" version = "v0.2.0" +[[projects]] + branch = "endpoint-186" + digest = "1:b56e65cab256427453f4454af3e7be2c1c544b52332772dbf7e6992aa7fdf599" + name = "github.com/dnote/dnote" + packages = ["pkg/assert"] + pruneopts = "" + revision = "54646c6bf85fba5efb8b4de67c9fed19cdbe0d97" + [[projects]] digest = "1:529d738b7976c3848cae5cf3a8036440166835e389c1f617af701eeb12a0518d" name = "github.com/golang/protobuf" @@ -159,6 +167,7 @@ "customer", "form", "paymentsource", + "source", "sub", "webhook", ] @@ -247,6 +256,7 @@ analyzer-version = 1 input-imports = [ "github.com/aymerick/douceur/inliner", + "github.com/dnote/dnote/pkg/assert", "github.com/gorilla/mux", "github.com/gorilla/securecookie", "github.com/gorilla/sessions", @@ -264,6 +274,7 @@ "github.com/stripe/stripe-go/card", "github.com/stripe/stripe-go/customer", "github.com/stripe/stripe-go/paymentsource", + "github.com/stripe/stripe-go/source", "github.com/stripe/stripe-go/sub", "github.com/stripe/stripe-go/webhook", "golang.org/x/crypto/bcrypt", diff --git a/server/Gopkg.toml b/pkg/server/Gopkg.toml similarity index 100% rename from server/Gopkg.toml rename to pkg/server/Gopkg.toml diff --git a/server/README.md b/pkg/server/README.md similarity index 100% rename from server/README.md rename to pkg/server/README.md diff --git a/server/api/.env.dev b/pkg/server/api/.env.dev similarity index 100% rename from server/api/.env.dev rename to pkg/server/api/.env.dev diff --git a/server/api/.env.test b/pkg/server/api/.env.test similarity index 95% rename from server/api/.env.test rename to pkg/server/api/.env.test index 976dcecd..9342b643 100644 --- a/server/api/.env.test +++ b/pkg/server/api/.env.test @@ -5,7 +5,7 @@ GO_ENV=DEVELOPMENT DB_HOST=localhost POSTGRES_DB=dnote_test -POSTGRES_USER=sung +POSTGRES_USER=postgres SmtpUsername=mock-SmtpUsername SmtpPassword=mock-SmtpPassword diff --git a/server/api/.gitignore b/pkg/server/api/.gitignore similarity index 100% rename from server/api/.gitignore rename to pkg/server/api/.gitignore diff --git a/server/api/README.md b/pkg/server/api/README.md similarity index 100% rename from server/api/README.md rename to pkg/server/api/README.md diff --git a/server/api/clock/clock.go b/pkg/server/api/clock/clock.go similarity index 100% rename from server/api/clock/clock.go rename to pkg/server/api/clock/clock.go diff --git a/server/api/crypt/crypt.go b/pkg/server/api/crypt/crypt.go similarity index 100% rename from server/api/crypt/crypt.go rename to pkg/server/api/crypt/crypt.go diff --git a/server/api/handlers/auth.go b/pkg/server/api/handlers/auth.go similarity index 98% rename from server/api/handlers/auth.go rename to pkg/server/api/handlers/auth.go index 827f54fa..0c34cf6a 100644 --- a/server/api/handlers/auth.go +++ b/pkg/server/api/handlers/auth.go @@ -24,9 +24,9 @@ import ( "net/http" "time" - "github.com/dnote/dnote/server/api/helpers" - "github.com/dnote/dnote/server/api/operations" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/helpers" + "github.com/dnote/dnote/pkg/server/api/operations" + "github.com/dnote/dnote/pkg/server/database" "github.com/jinzhu/gorm" "github.com/markbates/goth" "github.com/markbates/goth/gothic" diff --git a/server/api/handlers/digests.go b/pkg/server/api/handlers/digests.go similarity index 95% rename from server/api/handlers/digests.go rename to pkg/server/api/handlers/digests.go index 4d363f6d..8a12b6ad 100644 --- a/server/api/handlers/digests.go +++ b/pkg/server/api/handlers/digests.go @@ -23,10 +23,10 @@ import ( "net/http" "strconv" - "github.com/dnote/dnote/server/api/helpers" - "github.com/dnote/dnote/server/api/logger" - "github.com/dnote/dnote/server/api/presenters" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/helpers" + "github.com/dnote/dnote/pkg/server/api/logger" + "github.com/dnote/dnote/pkg/server/api/presenters" + "github.com/dnote/dnote/pkg/server/database" "github.com/gorilla/mux" "github.com/pkg/errors" ) diff --git a/server/api/handlers/health.go b/pkg/server/api/handlers/health.go similarity index 100% rename from server/api/handlers/health.go rename to pkg/server/api/handlers/health.go diff --git a/server/api/handlers/helpers.go b/pkg/server/api/handlers/helpers.go similarity index 96% rename from server/api/handlers/helpers.go rename to pkg/server/api/handlers/helpers.go index cc2f26a4..fe3d7021 100644 --- a/server/api/handlers/helpers.go +++ b/pkg/server/api/handlers/helpers.go @@ -24,8 +24,8 @@ import ( "net/http" "strings" - "github.com/dnote/dnote/server/api/logger" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/logger" + "github.com/dnote/dnote/pkg/server/database" "github.com/jinzhu/gorm" "github.com/pkg/errors" ) diff --git a/server/api/handlers/limit.go b/pkg/server/api/handlers/limit.go similarity index 100% rename from server/api/handlers/limit.go rename to pkg/server/api/handlers/limit.go diff --git a/server/api/handlers/notes.go b/pkg/server/api/handlers/notes.go similarity index 98% rename from server/api/handlers/notes.go rename to pkg/server/api/handlers/notes.go index b9937f7c..6d34f353 100644 --- a/server/api/handlers/notes.go +++ b/pkg/server/api/handlers/notes.go @@ -26,9 +26,9 @@ import ( "strings" "time" - "github.com/dnote/dnote/server/api/helpers" - "github.com/dnote/dnote/server/api/presenters" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/helpers" + "github.com/dnote/dnote/pkg/server/api/presenters" + "github.com/dnote/dnote/pkg/server/database" "github.com/gorilla/mux" "github.com/jinzhu/gorm" "github.com/pkg/errors" diff --git a/server/api/handlers/routes.go b/pkg/server/api/handlers/routes.go similarity index 98% rename from server/api/handlers/routes.go rename to pkg/server/api/handlers/routes.go index ebc28ea0..30ac3004 100644 --- a/server/api/handlers/routes.go +++ b/pkg/server/api/handlers/routes.go @@ -26,10 +26,10 @@ import ( "strings" "time" - "github.com/dnote/dnote/server/api/clock" - "github.com/dnote/dnote/server/api/helpers" - "github.com/dnote/dnote/server/api/logger" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/clock" + "github.com/dnote/dnote/pkg/server/api/helpers" + "github.com/dnote/dnote/pkg/server/api/logger" + "github.com/dnote/dnote/pkg/server/database" "github.com/gorilla/mux" "github.com/markbates/goth/gothic" "github.com/pkg/errors" diff --git a/server/api/handlers/routes_test.go b/pkg/server/api/handlers/routes_test.go similarity index 90% rename from server/api/handlers/routes_test.go rename to pkg/server/api/handlers/routes_test.go index 4305b970..43ae67dc 100644 --- a/server/api/handlers/routes_test.go +++ b/pkg/server/api/handlers/routes_test.go @@ -25,8 +25,9 @@ import ( "testing" "time" - "github.com/dnote/dnote/server/database" - "github.com/dnote/dnote/server/testutils" + "github.com/dnote/dnote/pkg/assert" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/testutils" "github.com/pkg/errors" ) @@ -78,7 +79,7 @@ func TestGetSessionKeyFromCookie(t *testing.T) { t.Fatal(errors.Wrap(err, "executing")) } - testutils.AssertEqual(t, got, tc.expected, "result mismatch") + assert.Equal(t, got, tc.expected, "result mismatch") } } @@ -108,7 +109,7 @@ func TestGetSessionKeyFromAuth(t *testing.T) { t.Fatal(errors.Wrap(err, "executing")) } - testutils.AssertEqual(t, got, tc.expected, "result mismatch") + assert.Equal(t, got, tc.expected, "result mismatch") } } @@ -178,7 +179,7 @@ func TestGetCredential(t *testing.T) { t.Fatal(errors.Wrap(err, "executing")) } - testutils.AssertEqual(t, got, tc.expected, "result mismatch") + assert.Equal(t, got, tc.expected, "result mismatch") } } @@ -234,7 +235,7 @@ func TestAuthMiddleware(t *testing.T) { res := testutils.HTTPDo(t, req) // test - testutils.AssertEqual(t, res.StatusCode, tc.expectedStatus, "status code mismatch") + assert.Equal(t, res.StatusCode, tc.expectedStatus, "status code mismatch") }) } }) @@ -279,7 +280,7 @@ func TestAuthMiddleware(t *testing.T) { res := testutils.HTTPDo(t, req) // test - testutils.AssertEqual(t, res.StatusCode, tc.expectedStatus, "status code mismatch") + assert.Equal(t, res.StatusCode, tc.expectedStatus, "status code mismatch") }) } }) @@ -291,7 +292,7 @@ func TestAuthMiddleware(t *testing.T) { res := testutils.HTTPDo(t, req) // test - testutils.AssertEqual(t, res.StatusCode, http.StatusUnauthorized, "status code mismatch") + assert.Equal(t, res.StatusCode, http.StatusUnauthorized, "status code mismatch") }) } @@ -342,7 +343,7 @@ func TestTokenAuthMiddleWare(t *testing.T) { res := testutils.HTTPDo(t, req) // test - testutils.AssertEqual(t, res.StatusCode, tc.expectedStatus, "status code mismatch") + assert.Equal(t, res.StatusCode, tc.expectedStatus, "status code mismatch") }) } }) @@ -371,7 +372,7 @@ func TestTokenAuthMiddleWare(t *testing.T) { res := testutils.HTTPDo(t, req) // test - testutils.AssertEqual(t, res.StatusCode, tc.expectedStatus, "status code mismatch") + assert.Equal(t, res.StatusCode, tc.expectedStatus, "status code mismatch") }) } }) @@ -408,7 +409,7 @@ func TestTokenAuthMiddleWare(t *testing.T) { res := testutils.HTTPDo(t, req) // test - testutils.AssertEqual(t, res.StatusCode, tc.expectedStatus, "status code mismatch") + assert.Equal(t, res.StatusCode, tc.expectedStatus, "status code mismatch") }) } }) @@ -420,6 +421,6 @@ func TestTokenAuthMiddleWare(t *testing.T) { res := testutils.HTTPDo(t, req) // test - testutils.AssertEqual(t, res.StatusCode, http.StatusUnauthorized, "status code mismatch") + assert.Equal(t, res.StatusCode, http.StatusUnauthorized, "status code mismatch") }) } diff --git a/server/api/handlers/semver.go b/pkg/server/api/handlers/semver.go similarity index 100% rename from server/api/handlers/semver.go rename to pkg/server/api/handlers/semver.go diff --git a/server/api/handlers/subscription.go b/pkg/server/api/handlers/subscription.go similarity index 98% rename from server/api/handlers/subscription.go rename to pkg/server/api/handlers/subscription.go index 5a0614da..d77c19be 100644 --- a/server/api/handlers/subscription.go +++ b/pkg/server/api/handlers/subscription.go @@ -26,9 +26,9 @@ import ( "os" "strings" - "github.com/dnote/dnote/server/api/helpers" - "github.com/dnote/dnote/server/api/operations" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/helpers" + "github.com/dnote/dnote/pkg/server/api/operations" + "github.com/dnote/dnote/pkg/server/database" "github.com/jinzhu/gorm" "github.com/pkg/errors" "github.com/stripe/stripe-go" diff --git a/server/api/handlers/user.go b/pkg/server/api/handlers/user.go similarity index 98% rename from server/api/handlers/user.go rename to pkg/server/api/handlers/user.go index 9c402cf3..8af7e1f3 100644 --- a/server/api/handlers/user.go +++ b/pkg/server/api/handlers/user.go @@ -23,11 +23,11 @@ import ( "net/http" "time" - "github.com/dnote/dnote/server/api/crypt" - "github.com/dnote/dnote/server/api/helpers" - "github.com/dnote/dnote/server/api/operations" - "github.com/dnote/dnote/server/database" - "github.com/dnote/dnote/server/mailer" + "github.com/dnote/dnote/pkg/server/api/crypt" + "github.com/dnote/dnote/pkg/server/api/helpers" + "github.com/dnote/dnote/pkg/server/api/operations" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/mailer" "github.com/pkg/errors" ) diff --git a/server/api/handlers/v1_auth.go b/pkg/server/api/handlers/v1_auth.go similarity index 98% rename from server/api/handlers/v1_auth.go rename to pkg/server/api/handlers/v1_auth.go index 62decdf1..659dd695 100644 --- a/server/api/handlers/v1_auth.go +++ b/pkg/server/api/handlers/v1_auth.go @@ -25,9 +25,9 @@ import ( "github.com/pkg/errors" - "github.com/dnote/dnote/server/api/crypt" - "github.com/dnote/dnote/server/api/operations" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/crypt" + "github.com/dnote/dnote/pkg/server/api/operations" + "github.com/dnote/dnote/pkg/server/database" ) // ErrLoginFailure is an error for failed login diff --git a/server/api/handlers/v1_books.go b/pkg/server/api/handlers/v1_books.go similarity index 97% rename from server/api/handlers/v1_books.go rename to pkg/server/api/handlers/v1_books.go index b0e638b1..b016e2d4 100644 --- a/server/api/handlers/v1_books.go +++ b/pkg/server/api/handlers/v1_books.go @@ -24,10 +24,10 @@ import ( "net/http" "net/url" - "github.com/dnote/dnote/server/api/helpers" - "github.com/dnote/dnote/server/api/operations" - "github.com/dnote/dnote/server/api/presenters" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/helpers" + "github.com/dnote/dnote/pkg/server/api/operations" + "github.com/dnote/dnote/pkg/server/api/presenters" + "github.com/dnote/dnote/pkg/server/database" "github.com/gorilla/mux" "github.com/pkg/errors" ) diff --git a/server/api/handlers/v1_notes.go b/pkg/server/api/handlers/v1_notes.go similarity index 95% rename from server/api/handlers/v1_notes.go rename to pkg/server/api/handlers/v1_notes.go index 03d42732..84d51eba 100644 --- a/server/api/handlers/v1_notes.go +++ b/pkg/server/api/handlers/v1_notes.go @@ -22,10 +22,10 @@ import ( "encoding/json" "net/http" - "github.com/dnote/dnote/server/api/helpers" - "github.com/dnote/dnote/server/api/operations" - "github.com/dnote/dnote/server/api/presenters" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/helpers" + "github.com/dnote/dnote/pkg/server/api/operations" + "github.com/dnote/dnote/pkg/server/api/presenters" + "github.com/dnote/dnote/pkg/server/database" "github.com/gorilla/mux" "github.com/pkg/errors" ) diff --git a/server/api/handlers/v1_sync.go b/pkg/server/api/handlers/v1_sync.go similarity index 98% rename from server/api/handlers/v1_sync.go rename to pkg/server/api/handlers/v1_sync.go index b281983c..13e94f54 100644 --- a/server/api/handlers/v1_sync.go +++ b/pkg/server/api/handlers/v1_sync.go @@ -27,8 +27,8 @@ import ( "strconv" "time" - "github.com/dnote/dnote/server/api/helpers" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/helpers" + "github.com/dnote/dnote/pkg/server/database" "github.com/pkg/errors" ) diff --git a/server/api/handlers/v1_sync_test.go b/pkg/server/api/handlers/v1_sync_test.go similarity index 84% rename from server/api/handlers/v1_sync_test.go rename to pkg/server/api/handlers/v1_sync_test.go index 016cba7f..69e20ee6 100644 --- a/server/api/handlers/v1_sync_test.go +++ b/pkg/server/api/handlers/v1_sync_test.go @@ -24,7 +24,7 @@ import ( "reflect" "testing" - "github.com/dnote/dnote/server/testutils" + "github.com/dnote/dnote/pkg/assert" "github.com/pkg/errors" ) @@ -85,9 +85,9 @@ func TestParseGetSyncFragmentQuery(t *testing.T) { afterUSN, limit, err := parseGetSyncFragmentQuery(q) ok := reflect.DeepEqual(err, tc.err) - testutils.AssertEqual(t, ok, true, fmt.Sprintf("err mismatch for test case %d. Expected: %+v. Got: %+v", idx, tc.err, err)) + assert.Equal(t, ok, true, fmt.Sprintf("err mismatch for test case %d. Expected: %+v. Got: %+v", idx, tc.err, err)) - testutils.AssertEqual(t, afterUSN, tc.afterUSN, fmt.Sprintf("afterUSN mismatch for test case %d", idx)) - testutils.AssertEqual(t, limit, tc.limit, fmt.Sprintf("limit mismatch for test case %d", idx)) + assert.Equal(t, afterUSN, tc.afterUSN, fmt.Sprintf("afterUSN mismatch for test case %d", idx)) + assert.Equal(t, limit, tc.limit, fmt.Sprintf("limit mismatch for test case %d", idx)) } } diff --git a/server/api/handlers/v2_books.go b/pkg/server/api/handlers/v2_books.go similarity index 93% rename from server/api/handlers/v2_books.go rename to pkg/server/api/handlers/v2_books.go index efce3960..cd77dae1 100644 --- a/server/api/handlers/v2_books.go +++ b/pkg/server/api/handlers/v2_books.go @@ -22,10 +22,10 @@ import ( "encoding/json" "net/http" - "github.com/dnote/dnote/server/api/helpers" - "github.com/dnote/dnote/server/api/operations" - "github.com/dnote/dnote/server/api/presenters" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/helpers" + "github.com/dnote/dnote/pkg/server/api/operations" + "github.com/dnote/dnote/pkg/server/api/presenters" + "github.com/dnote/dnote/pkg/server/database" "github.com/pkg/errors" ) diff --git a/server/api/handlers/v2_notes.go b/pkg/server/api/handlers/v2_notes.go similarity index 93% rename from server/api/handlers/v2_notes.go rename to pkg/server/api/handlers/v2_notes.go index ae310f5b..85920d98 100644 --- a/server/api/handlers/v2_notes.go +++ b/pkg/server/api/handlers/v2_notes.go @@ -22,10 +22,10 @@ import ( "encoding/json" "net/http" - "github.com/dnote/dnote/server/api/helpers" - "github.com/dnote/dnote/server/api/operations" - "github.com/dnote/dnote/server/api/presenters" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/helpers" + "github.com/dnote/dnote/pkg/server/api/operations" + "github.com/dnote/dnote/pkg/server/api/presenters" + "github.com/dnote/dnote/pkg/server/database" "github.com/pkg/errors" ) diff --git a/server/api/helpers/const.go b/pkg/server/api/helpers/const.go similarity index 100% rename from server/api/helpers/const.go rename to pkg/server/api/helpers/const.go diff --git a/server/api/helpers/helpers.go b/pkg/server/api/helpers/helpers.go similarity index 96% rename from server/api/helpers/helpers.go rename to pkg/server/api/helpers/helpers.go index 9289034f..ea14b8a7 100644 --- a/server/api/helpers/helpers.go +++ b/pkg/server/api/helpers/helpers.go @@ -19,7 +19,7 @@ package helpers import ( - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/database" "github.com/pkg/errors" "github.com/satori/go.uuid" ) diff --git a/server/api/logger/main.go b/pkg/server/api/logger/main.go similarity index 100% rename from server/api/logger/main.go rename to pkg/server/api/logger/main.go diff --git a/server/api/main.go b/pkg/server/api/main.go similarity index 91% rename from server/api/main.go rename to pkg/server/api/main.go index 2047d3fd..819a326a 100644 --- a/server/api/main.go +++ b/pkg/server/api/main.go @@ -25,11 +25,11 @@ import ( "net/http" "os" - "github.com/dnote/dnote/server/api/clock" - "github.com/dnote/dnote/server/api/handlers" - "github.com/dnote/dnote/server/api/logger" - "github.com/dnote/dnote/server/database" - "github.com/dnote/dnote/server/mailer" + "github.com/dnote/dnote/pkg/server/api/clock" + "github.com/dnote/dnote/pkg/server/api/handlers" + "github.com/dnote/dnote/pkg/server/api/logger" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/mailer" "github.com/gorilla/mux" "github.com/gorilla/securecookie" diff --git a/server/api/operations/books.go b/pkg/server/api/operations/books.go similarity index 95% rename from server/api/operations/books.go rename to pkg/server/api/operations/books.go index 5f8b0734..b11721df 100644 --- a/server/api/operations/books.go +++ b/pkg/server/api/operations/books.go @@ -19,9 +19,9 @@ package operations import ( - "github.com/dnote/dnote/server/api/clock" - "github.com/dnote/dnote/server/api/helpers" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/clock" + "github.com/dnote/dnote/pkg/server/api/helpers" + "github.com/dnote/dnote/pkg/server/database" "github.com/jinzhu/gorm" "github.com/pkg/errors" ) diff --git a/server/api/operations/books_test.go b/pkg/server/api/operations/books_test.go similarity index 69% rename from server/api/operations/books_test.go rename to pkg/server/api/operations/books_test.go index 9887a98d..005f25d2 100644 --- a/server/api/operations/books_test.go +++ b/pkg/server/api/operations/books_test.go @@ -22,9 +22,10 @@ import ( "fmt" "testing" - "github.com/dnote/dnote/server/api/clock" - "github.com/dnote/dnote/server/database" - "github.com/dnote/dnote/server/testutils" + "github.com/dnote/dnote/pkg/assert" + "github.com/dnote/dnote/pkg/server/api/clock" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/testutils" "github.com/pkg/errors" ) @@ -87,16 +88,16 @@ func TestCreateBook(t *testing.T) { t.Fatal(errors.Wrap(err, "finding user")) } - testutils.AssertEqual(t, bookCount, 1, "book count mismatch") - testutils.AssertEqual(t, bookRecord.UserID, user.ID, "book user_id mismatch") - testutils.AssertEqual(t, bookRecord.Label, tc.label, "book label mismatch") - testutils.AssertEqual(t, bookRecord.USN, tc.expectedUSN, "book label mismatch") + assert.Equal(t, bookCount, 1, "book count mismatch") + assert.Equal(t, bookRecord.UserID, user.ID, "book user_id mismatch") + assert.Equal(t, bookRecord.Label, tc.label, "book label mismatch") + assert.Equal(t, bookRecord.USN, tc.expectedUSN, "book label mismatch") - testutils.AssertNotEqual(t, book.UUID, "", "book uuid should have been generated") - testutils.AssertEqual(t, book.UserID, user.ID, "returned book user_id mismatch") - testutils.AssertEqual(t, book.Label, tc.label, "returned book label mismatch") - testutils.AssertEqual(t, book.USN, tc.expectedUSN, "returned book usn mismatch") - testutils.AssertEqual(t, userRecord.MaxUSN, tc.expectedUSN, "user max_usn mismatch") + assert.NotEqual(t, book.UUID, "", "book uuid should have been generated") + assert.Equal(t, book.UserID, user.ID, "returned book user_id mismatch") + assert.Equal(t, book.Label, tc.label, "returned book label mismatch") + assert.Equal(t, book.USN, tc.expectedUSN, "returned book usn mismatch") + assert.Equal(t, userRecord.MaxUSN, tc.expectedUSN, "user max_usn mismatch") }() } } @@ -150,18 +151,18 @@ func TestDeleteBook(t *testing.T) { testutils.MustExec(t, db.First(&bookRecord), fmt.Sprintf("finding book for test case %d", idx)) testutils.MustExec(t, db.Where("id = ?", user.ID).First(&userRecord), fmt.Sprintf("finding user for test case %d", idx)) - testutils.AssertEqual(t, bookCount, 1, "book count mismatch") - testutils.AssertEqual(t, bookRecord.UserID, user.ID, "book user_id mismatch") - testutils.AssertEqual(t, bookRecord.Label, "", "book label mismatch") - testutils.AssertEqual(t, bookRecord.Deleted, true, "book deleted flag mismatch") - testutils.AssertEqual(t, bookRecord.USN, tc.expectedUSN, "book label mismatch") + assert.Equal(t, bookCount, 1, "book count mismatch") + assert.Equal(t, bookRecord.UserID, user.ID, "book user_id mismatch") + assert.Equal(t, bookRecord.Label, "", "book label mismatch") + assert.Equal(t, bookRecord.Deleted, true, "book deleted flag mismatch") + assert.Equal(t, bookRecord.USN, tc.expectedUSN, "book label mismatch") - testutils.AssertEqual(t, ret.UserID, user.ID, "returned book user_id mismatch") - testutils.AssertEqual(t, ret.Label, "", "returned book label mismatch") - testutils.AssertEqual(t, ret.Deleted, true, "returned book deleted flag mismatch") - testutils.AssertEqual(t, ret.USN, tc.expectedUSN, "returned book label mismatch") + assert.Equal(t, ret.UserID, user.ID, "returned book user_id mismatch") + assert.Equal(t, ret.Label, "", "returned book label mismatch") + assert.Equal(t, ret.Deleted, true, "returned book deleted flag mismatch") + assert.Equal(t, ret.USN, tc.expectedUSN, "returned book label mismatch") - testutils.AssertEqual(t, userRecord.MaxUSN, tc.expectedUSN, "user max_usn mismatch") + assert.Equal(t, userRecord.MaxUSN, tc.expectedUSN, "user max_usn mismatch") }() } } @@ -231,18 +232,18 @@ func TestUpdateBook(t *testing.T) { testutils.MustExec(t, db.First(&bookRecord), fmt.Sprintf("finding book for test case %d", idx)) testutils.MustExec(t, db.Where("id = ?", user.ID).First(&userRecord), fmt.Sprintf("finding user for test case %d", idx)) - testutils.AssertEqual(t, bookCount, 1, "book count mismatch") + assert.Equal(t, bookCount, 1, "book count mismatch") - testutils.AssertEqual(t, bookRecord.UserID, user.ID, "book user_id mismatch") - testutils.AssertEqual(t, bookRecord.Label, tc.expectedLabel, "book label mismatch") - testutils.AssertEqual(t, bookRecord.USN, tc.expectedUSN, "book label mismatch") - testutils.AssertEqual(t, bookRecord.EditedOn, c.Now().UnixNano(), "book edited_on mismatch") - testutils.AssertEqual(t, book.UserID, user.ID, "returned book user_id mismatch") - testutils.AssertEqual(t, book.Label, tc.expectedLabel, "returned book label mismatch") - testutils.AssertEqual(t, book.USN, tc.expectedUSN, "returned book usn mismatch") - testutils.AssertEqual(t, book.EditedOn, c.Now().UnixNano(), "returned book edited_on mismatch") + assert.Equal(t, bookRecord.UserID, user.ID, "book user_id mismatch") + assert.Equal(t, bookRecord.Label, tc.expectedLabel, "book label mismatch") + assert.Equal(t, bookRecord.USN, tc.expectedUSN, "book label mismatch") + assert.Equal(t, bookRecord.EditedOn, c.Now().UnixNano(), "book edited_on mismatch") + assert.Equal(t, book.UserID, user.ID, "returned book user_id mismatch") + assert.Equal(t, book.Label, tc.expectedLabel, "returned book label mismatch") + assert.Equal(t, book.USN, tc.expectedUSN, "returned book usn mismatch") + assert.Equal(t, book.EditedOn, c.Now().UnixNano(), "returned book edited_on mismatch") - testutils.AssertEqual(t, userRecord.MaxUSN, tc.expectedUserUSN, "user max_usn mismatch") + assert.Equal(t, userRecord.MaxUSN, tc.expectedUserUSN, "user max_usn mismatch") }() } } diff --git a/server/api/operations/helpers.go b/pkg/server/api/operations/helpers.go similarity index 96% rename from server/api/operations/helpers.go rename to pkg/server/api/operations/helpers.go index 94ecb09a..7f748191 100644 --- a/server/api/operations/helpers.go +++ b/pkg/server/api/operations/helpers.go @@ -19,7 +19,7 @@ package operations import ( - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/database" "github.com/jinzhu/gorm" "github.com/pkg/errors" ) diff --git a/server/api/operations/helpers_test.go b/pkg/server/api/operations/helpers_test.go similarity index 83% rename from server/api/operations/helpers_test.go rename to pkg/server/api/operations/helpers_test.go index ea3e62de..cb3bee1e 100644 --- a/server/api/operations/helpers_test.go +++ b/pkg/server/api/operations/helpers_test.go @@ -22,8 +22,9 @@ import ( "fmt" "testing" - "github.com/dnote/dnote/server/database" - "github.com/dnote/dnote/server/testutils" + "github.com/dnote/dnote/pkg/assert" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/testutils" "github.com/pkg/errors" ) @@ -67,8 +68,8 @@ func TestIncremenetUserUSN(t *testing.T) { var userRecord database.User testutils.MustExec(t, db.Where("id = ?", user.ID).First(&userRecord), fmt.Sprintf("finding user for test case %d", idx)) - testutils.AssertEqual(t, userRecord.MaxUSN, tc.expectedMaxUSN, fmt.Sprintf("user max_usn mismatch for case %d", idx)) - testutils.AssertEqual(t, nextUSN, tc.expectedMaxUSN, fmt.Sprintf("next_usn mismatch for case %d", idx)) + assert.Equal(t, userRecord.MaxUSN, tc.expectedMaxUSN, fmt.Sprintf("user max_usn mismatch for case %d", idx)) + assert.Equal(t, nextUSN, tc.expectedMaxUSN, fmt.Sprintf("next_usn mismatch for case %d", idx)) }() } } diff --git a/server/api/operations/notes.go b/pkg/server/api/operations/notes.go similarity index 95% rename from server/api/operations/notes.go rename to pkg/server/api/operations/notes.go index 7496e94a..8569014e 100644 --- a/server/api/operations/notes.go +++ b/pkg/server/api/operations/notes.go @@ -19,9 +19,9 @@ package operations import ( - "github.com/dnote/dnote/server/api/clock" - "github.com/dnote/dnote/server/api/helpers" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/clock" + "github.com/dnote/dnote/pkg/server/api/helpers" + "github.com/dnote/dnote/pkg/server/database" "github.com/jinzhu/gorm" "github.com/pkg/errors" ) diff --git a/server/api/operations/notes_test.go b/pkg/server/api/operations/notes_test.go similarity index 74% rename from server/api/operations/notes_test.go rename to pkg/server/api/operations/notes_test.go index c97b9467..fb4b4f4b 100644 --- a/server/api/operations/notes_test.go +++ b/pkg/server/api/operations/notes_test.go @@ -23,9 +23,10 @@ import ( "testing" "time" - "github.com/dnote/dnote/server/api/clock" - "github.com/dnote/dnote/server/database" - "github.com/dnote/dnote/server/testutils" + "github.com/dnote/dnote/pkg/assert" + "github.com/dnote/dnote/pkg/server/api/clock" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/testutils" "github.com/pkg/errors" ) @@ -105,17 +106,17 @@ func TestCreateNote(t *testing.T) { testutils.MustExec(t, db.First(¬eRecord), fmt.Sprintf("finding note for test case %d", idx)) testutils.MustExec(t, db.Where("id = ?", user.ID).First(&userRecord), fmt.Sprintf("finding user for test case %d", idx)) - testutils.AssertEqual(t, bookCount, 1, "book count mismatch") - testutils.AssertEqual(t, noteCount, 1, "note count mismatch") - testutils.AssertNotEqual(t, noteRecord.UUID, "", "note UUID should have been generated") - testutils.AssertEqual(t, noteRecord.UserID, user.ID, "note UserID mismatch") - testutils.AssertEqual(t, noteRecord.Body, "note content", "note Body mismatch") - testutils.AssertEqual(t, noteRecord.Deleted, false, "note Deleted mismatch") - testutils.AssertEqual(t, noteRecord.USN, tc.expectedUSN, "note Label mismatch") - testutils.AssertEqual(t, noteRecord.AddedOn, tc.expectedAddedOn, "note AddedOn mismatch") - testutils.AssertEqual(t, noteRecord.EditedOn, tc.expectedEditedOn, "note EditedOn mismatch") + assert.Equal(t, bookCount, 1, "book count mismatch") + assert.Equal(t, noteCount, 1, "note count mismatch") + assert.NotEqual(t, noteRecord.UUID, "", "note UUID should have been generated") + assert.Equal(t, noteRecord.UserID, user.ID, "note UserID mismatch") + assert.Equal(t, noteRecord.Body, "note content", "note Body mismatch") + assert.Equal(t, noteRecord.Deleted, false, "note Deleted mismatch") + assert.Equal(t, noteRecord.USN, tc.expectedUSN, "note Label mismatch") + assert.Equal(t, noteRecord.AddedOn, tc.expectedAddedOn, "note AddedOn mismatch") + assert.Equal(t, noteRecord.EditedOn, tc.expectedEditedOn, "note EditedOn mismatch") - testutils.AssertEqual(t, userRecord.MaxUSN, tc.expectedUSN, "user max_usn mismatch") + assert.Equal(t, userRecord.MaxUSN, tc.expectedUSN, "user max_usn mismatch") }() } } @@ -160,7 +161,7 @@ func TestUpdateNote(t *testing.T) { content := "updated test content" tx := db.Begin() - if _, err := UpdateNote(tx, user, c, note, nil, &content, nil); err != nil { + if _, err := UpdateNote(tx, user, c, note, nil, &content); err != nil { tx.Rollback() t.Fatal(errors.Wrap(err, "deleting note")) } @@ -175,14 +176,14 @@ func TestUpdateNote(t *testing.T) { testutils.MustExec(t, db.First(¬eRecord), fmt.Sprintf("finding note for test case %d", idx)) testutils.MustExec(t, db.Where("id = ?", user.ID).First(&userRecord), fmt.Sprintf("finding user for test case %d", idx)) - testutils.AssertEqual(t, bookCount, 1, "book count mismatch") - testutils.AssertEqual(t, noteCount, 1, "note count mismatch") - testutils.AssertEqual(t, noteRecord.UserID, user.ID, "note UserID mismatch") - testutils.AssertEqual(t, noteRecord.Body, content, "note Body mismatch") - testutils.AssertEqual(t, noteRecord.Deleted, false, "note Deleted mismatch") - testutils.AssertEqual(t, noteRecord.USN, tc.expectedUSN, "note USN mismatch") + assert.Equal(t, bookCount, 1, "book count mismatch") + assert.Equal(t, noteCount, 1, "note count mismatch") + assert.Equal(t, noteRecord.UserID, user.ID, "note UserID mismatch") + assert.Equal(t, noteRecord.Body, content, "note Body mismatch") + assert.Equal(t, noteRecord.Deleted, false, "note Deleted mismatch") + assert.Equal(t, noteRecord.USN, tc.expectedUSN, "note USN mismatch") - testutils.AssertEqual(t, userRecord.MaxUSN, tc.expectedUSN, "user MaxUSN mismatch") + assert.Equal(t, userRecord.MaxUSN, tc.expectedUSN, "user MaxUSN mismatch") }() } } @@ -239,18 +240,18 @@ func TestDeleteNote(t *testing.T) { testutils.MustExec(t, db.First(¬eRecord), fmt.Sprintf("finding note for test case %d", idx)) testutils.MustExec(t, db.Where("id = ?", user.ID).First(&userRecord), fmt.Sprintf("finding user for test case %d", idx)) - testutils.AssertEqual(t, noteCount, 1, "note count mismatch") + assert.Equal(t, noteCount, 1, "note count mismatch") - testutils.AssertEqual(t, noteRecord.UserID, user.ID, "note user_id mismatch") - testutils.AssertEqual(t, noteRecord.Body, "", "note content mismatch") - testutils.AssertEqual(t, noteRecord.Deleted, true, "note deleted flag mismatch") - testutils.AssertEqual(t, noteRecord.USN, tc.expectedUSN, "note label mismatch") - testutils.AssertEqual(t, userRecord.MaxUSN, tc.expectedUSN, "user max_usn mismatch") + assert.Equal(t, noteRecord.UserID, user.ID, "note user_id mismatch") + assert.Equal(t, noteRecord.Body, "", "note content mismatch") + assert.Equal(t, noteRecord.Deleted, true, "note deleted flag mismatch") + assert.Equal(t, noteRecord.USN, tc.expectedUSN, "note label mismatch") + assert.Equal(t, userRecord.MaxUSN, tc.expectedUSN, "user max_usn mismatch") - testutils.AssertEqual(t, ret.UserID, user.ID, "note user_id mismatch") - testutils.AssertEqual(t, ret.Body, "", "note content mismatch") - testutils.AssertEqual(t, ret.Deleted, true, "note deleted flag mismatch") - testutils.AssertEqual(t, ret.USN, tc.expectedUSN, "note label mismatch") + assert.Equal(t, ret.UserID, user.ID, "note user_id mismatch") + assert.Equal(t, ret.Body, "", "note content mismatch") + assert.Equal(t, ret.Deleted, true, "note deleted flag mismatch") + assert.Equal(t, ret.USN, tc.expectedUSN, "note label mismatch") }() } } diff --git a/server/api/operations/sessions.go b/pkg/server/api/operations/sessions.go similarity index 95% rename from server/api/operations/sessions.go rename to pkg/server/api/operations/sessions.go index 07e7237c..d9fb6cb6 100644 --- a/server/api/operations/sessions.go +++ b/pkg/server/api/operations/sessions.go @@ -23,8 +23,8 @@ import ( "github.com/jinzhu/gorm" "github.com/pkg/errors" - "github.com/dnote/dnote/server/api/crypt" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/crypt" + "github.com/dnote/dnote/pkg/server/database" ) // CreateSession returns a new session for the user of the given id diff --git a/server/api/operations/subscriptions.go b/pkg/server/api/operations/subscriptions.go similarity index 98% rename from server/api/operations/subscriptions.go rename to pkg/server/api/operations/subscriptions.go index 6eb60e34..e24da365 100644 --- a/server/api/operations/subscriptions.go +++ b/pkg/server/api/operations/subscriptions.go @@ -19,7 +19,7 @@ package operations import ( - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/database" "github.com/pkg/errors" "github.com/stripe/stripe-go" diff --git a/server/api/operations/users.go b/pkg/server/api/operations/users.go similarity index 97% rename from server/api/operations/users.go rename to pkg/server/api/operations/users.go index bff6f762..486bfd9d 100644 --- a/server/api/operations/users.go +++ b/pkg/server/api/operations/users.go @@ -21,8 +21,8 @@ package operations import ( "time" - "github.com/dnote/dnote/server/api/crypt" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/crypt" + "github.com/dnote/dnote/pkg/server/database" "github.com/jinzhu/gorm" "github.com/pkg/errors" ) diff --git a/server/api/presenters/presenters.go b/pkg/server/api/presenters/presenters.go similarity index 98% rename from server/api/presenters/presenters.go rename to pkg/server/api/presenters/presenters.go index 4074c835..b1eec729 100644 --- a/server/api/presenters/presenters.go +++ b/pkg/server/api/presenters/presenters.go @@ -21,7 +21,7 @@ package presenters import ( "time" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/database" ) // formatTs rounds up the given timestamp to the microsecond diff --git a/server/api/scripts/dev.sh b/pkg/server/api/scripts/dev.sh similarity index 86% rename from server/api/scripts/dev.sh rename to pkg/server/api/scripts/dev.sh index bbbbfe19..5293b7ee 100755 --- a/server/api/scripts/dev.sh +++ b/pkg/server/api/scripts/dev.sh @@ -3,7 +3,7 @@ # usage: DOTENV_PATH=path_to_dotenv_file EMAIL_TEMPLATE_DIR=path_to_email_templates ./dev.sh set -eux -basePath="$GOPATH/src/github.com/dnote/dnote/server" +basePath="$GOPATH/src/github.com/dnote/dnote/pkg/server" # load env export $(cat "$DOTENV_PATH" | xargs) diff --git a/server/api/scripts/makeDemoDigests/main.go b/pkg/server/api/scripts/makeDemoDigests/main.go similarity index 98% rename from server/api/scripts/makeDemoDigests/main.go rename to pkg/server/api/scripts/makeDemoDigests/main.go index d00a8059..0ba4d4be 100644 --- a/server/api/scripts/makeDemoDigests/main.go +++ b/pkg/server/api/scripts/makeDemoDigests/main.go @@ -19,8 +19,8 @@ package main import ( - "github.com/dnote/dnote/server/api/helpers" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/api/helpers" + "github.com/dnote/dnote/pkg/server/database" "time" ) diff --git a/server/api/scripts/setup.sh b/pkg/server/api/scripts/setup.sh similarity index 100% rename from server/api/scripts/setup.sh rename to pkg/server/api/scripts/setup.sh diff --git a/server/api/scripts/test-local.sh b/pkg/server/api/scripts/test-local.sh similarity index 71% rename from server/api/scripts/test-local.sh rename to pkg/server/api/scripts/test-local.sh index 4ec48692..ebe8b7bd 100755 --- a/server/api/scripts/test-local.sh +++ b/pkg/server/api/scripts/test-local.sh @@ -2,7 +2,7 @@ # test-local.sh runs api tests using local setting set -eux -basePath=$GOPATH/src/github.com/dnote/dnote/server/api +basePath=$GOPATH/src/github.com/dnote/dnote/pkg/server/api export $(cat "$basePath"/.env.test | xargs) "$basePath"/scripts/test.sh diff --git a/server/api/scripts/test.sh b/pkg/server/api/scripts/test.sh similarity index 75% rename from server/api/scripts/test.sh rename to pkg/server/api/scripts/test.sh index 48b78eb3..6e9d3fa8 100755 --- a/server/api/scripts/test.sh +++ b/pkg/server/api/scripts/test.sh @@ -3,6 +3,6 @@ # appropriate env vars. set -eux -pushd "$GOPATH"/src/github.com/dnote/dnote/server/api +pushd "$GOPATH"/src/github.com/dnote/dnote/pkg/server/api go test ./handlers/... ./operations/... -cover -p 1 popd diff --git a/server/database/main.go b/pkg/server/database/main.go similarity index 100% rename from server/database/main.go rename to pkg/server/database/main.go diff --git a/server/database/models.go b/pkg/server/database/models.go similarity index 100% rename from server/database/models.go rename to pkg/server/database/models.go diff --git a/server/database/types.go b/pkg/server/database/types.go similarity index 100% rename from server/database/types.go rename to pkg/server/database/types.go diff --git a/server/job/.env.dev b/pkg/server/job/.env.dev similarity index 100% rename from server/job/.env.dev rename to pkg/server/job/.env.dev diff --git a/server/job/.gitignore b/pkg/server/job/.gitignore similarity index 100% rename from server/job/.gitignore rename to pkg/server/job/.gitignore diff --git a/server/job/digest/digest.go b/pkg/server/job/digest/digest.go similarity index 97% rename from server/job/digest/digest.go rename to pkg/server/job/digest/digest.go index e78108cf..5446eb0d 100644 --- a/server/job/digest/digest.go +++ b/pkg/server/job/digest/digest.go @@ -22,8 +22,8 @@ import ( "log" "time" - "github.com/dnote/dnote/server/database" - "github.com/dnote/dnote/server/mailer" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/mailer" "github.com/pkg/errors" ) diff --git a/server/job/main.go b/pkg/server/job/main.go similarity index 92% rename from server/job/main.go rename to pkg/server/job/main.go index d3bfcff0..93f95bae 100644 --- a/server/job/main.go +++ b/pkg/server/job/main.go @@ -23,9 +23,9 @@ import ( "log" "os" - "github.com/dnote/dnote/server/database" - "github.com/dnote/dnote/server/job/digest" - "github.com/dnote/dnote/server/mailer" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/job/digest" + "github.com/dnote/dnote/pkg/server/mailer" "github.com/joho/godotenv" _ "github.com/lib/pq" diff --git a/server/job/scripts/dev.sh b/pkg/server/job/scripts/dev.sh similarity index 100% rename from server/job/scripts/dev.sh rename to pkg/server/job/scripts/dev.sh diff --git a/server/mailer/mailer.go b/pkg/server/mailer/mailer.go similarity index 100% rename from server/mailer/mailer.go rename to pkg/server/mailer/mailer.go diff --git a/server/mailer/templates/.env.dev b/pkg/server/mailer/templates/.env.dev similarity index 100% rename from server/mailer/templates/.env.dev rename to pkg/server/mailer/templates/.env.dev diff --git a/server/mailer/templates/.gitignore b/pkg/server/mailer/templates/.gitignore similarity index 100% rename from server/mailer/templates/.gitignore rename to pkg/server/mailer/templates/.gitignore diff --git a/server/mailer/templates/README.md b/pkg/server/mailer/templates/README.md similarity index 100% rename from server/mailer/templates/README.md rename to pkg/server/mailer/templates/README.md diff --git a/server/mailer/templates/main.go b/pkg/server/mailer/templates/main.go similarity index 94% rename from server/mailer/templates/main.go rename to pkg/server/mailer/templates/main.go index 24c12091..39048e6c 100644 --- a/server/mailer/templates/main.go +++ b/pkg/server/mailer/templates/main.go @@ -22,9 +22,9 @@ import ( "log" "net/http" - "github.com/dnote/dnote/server/database" - "github.com/dnote/dnote/server/job/digest" - "github.com/dnote/dnote/server/mailer" + "github.com/dnote/dnote/pkg/server/database" + "github.com/dnote/dnote/pkg/server/job/digest" + "github.com/dnote/dnote/pkg/server/mailer" "github.com/joho/godotenv" _ "github.com/lib/pq" "github.com/pkg/errors" diff --git a/server/mailer/templates/scripts/run.sh b/pkg/server/mailer/templates/scripts/run.sh similarity index 100% rename from server/mailer/templates/scripts/run.sh rename to pkg/server/mailer/templates/scripts/run.sh diff --git a/server/mailer/templates/src/email_verification.html b/pkg/server/mailer/templates/src/email_verification.html similarity index 100% rename from server/mailer/templates/src/email_verification.html rename to pkg/server/mailer/templates/src/email_verification.html diff --git a/server/mailer/templates/src/footer.html b/pkg/server/mailer/templates/src/footer.html similarity index 100% rename from server/mailer/templates/src/footer.html rename to pkg/server/mailer/templates/src/footer.html diff --git a/server/mailer/templates/src/header.html b/pkg/server/mailer/templates/src/header.html similarity index 100% rename from server/mailer/templates/src/header.html rename to pkg/server/mailer/templates/src/header.html diff --git a/server/mailer/templates/src/reset_password.html b/pkg/server/mailer/templates/src/reset_password.html similarity index 100% rename from server/mailer/templates/src/reset_password.html rename to pkg/server/mailer/templates/src/reset_password.html diff --git a/server/mailer/templates/src/weekly_digest.html b/pkg/server/mailer/templates/src/weekly_digest.html similarity index 100% rename from server/mailer/templates/src/weekly_digest.html rename to pkg/server/mailer/templates/src/weekly_digest.html diff --git a/server/mailer/types.go b/pkg/server/mailer/types.go similarity index 100% rename from server/mailer/types.go rename to pkg/server/mailer/types.go diff --git a/server/mailer/utils.go b/pkg/server/mailer/utils.go similarity index 97% rename from server/mailer/utils.go rename to pkg/server/mailer/utils.go index 1fb91ab4..e0510291 100644 --- a/server/mailer/utils.go +++ b/pkg/server/mailer/utils.go @@ -22,7 +22,7 @@ import ( "crypto/rand" "encoding/base64" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/database" "github.com/pkg/errors" ) diff --git a/server/testutils/main.go b/pkg/server/testutils/main.go similarity index 68% rename from server/testutils/main.go rename to pkg/server/testutils/main.go index 301a37e2..b049dd30 100644 --- a/server/testutils/main.go +++ b/pkg/server/testutils/main.go @@ -22,122 +22,19 @@ package testutils import ( "encoding/json" "fmt" - "io/ioutil" "net/http" "net/http/httptest" - "reflect" "strings" "testing" "time" - "github.com/dnote/dnote/server/database" + "github.com/dnote/dnote/pkg/server/database" "github.com/stripe/stripe-go" "github.com/jinzhu/gorm" "github.com/pkg/errors" ) -func checkEqual(a interface{}, b interface{}, message string) (bool, string) { - if a == b { - return true, "" - } - - var m string - if len(message) == 0 { - m = fmt.Sprintf("%v != %v", a, b) - } else { - m = message - } - errorMessage := fmt.Sprintf("%s. Actual: %+v. Expected: %+v.", m, a, b) - - return false, errorMessage -} - -// AssertEqual errors a test if the actual does not match the expected -func AssertEqual(t *testing.T, a interface{}, b interface{}, message string) { - ok, m := checkEqual(a, b, message) - if !ok { - t.Error(m) - } -} - -// AssertEqualf fails a test if the actual does not match the expected -func AssertEqualf(t *testing.T, a interface{}, b interface{}, message string) { - ok, m := checkEqual(a, b, message) - if !ok { - t.Fatal(m) - } -} - -// AssertNotEqual fails a test if the actual matches the expected -func AssertNotEqual(t *testing.T, a interface{}, b interface{}, message string) { - if a != b { - return - } - if len(message) == 0 { - message = fmt.Sprintf("%v != %v", a, b) - } - t.Errorf("%s. Expected %+v to not equal %+v.", message, a, b) -} - -// AssertDeepEqual fails a test if the actual does not deeply equal the expected -func AssertDeepEqual(t *testing.T, a interface{}, b interface{}, message string) { - if reflect.DeepEqual(a, b) { - return - } - - if len(message) == 0 { - message = fmt.Sprintf("%v != %v", a, b) - } - t.Errorf("%s.\nActual: %+v.\nExpected: %+v.", message, a, b) -} - -// AssertEqualJSON asserts that two JSON strings are equal -func AssertEqualJSON(t *testing.T, a, b, message string) { - var o1 interface{} - var o2 interface{} - - err := json.Unmarshal([]byte(a), &o1) - if err != nil { - panic(fmt.Errorf("Error mashalling string 1 :: %s", err.Error())) - } - err = json.Unmarshal([]byte(b), &o2) - if err != nil { - panic(fmt.Errorf("Error mashalling string 2 :: %s", err.Error())) - } - - if reflect.DeepEqual(o1, o2) { - return - } - - if len(message) == 0 { - message = fmt.Sprintf("%v != %v", a, b) - } - t.Errorf("%s.\nActual: %+v.\nExpected: %+v.", message, a, b) -} - -// ReadJSON reads JSON fixture to the struct at the destination address -func ReadJSON(path string, destination interface{}) { - var dat []byte - dat, err := ioutil.ReadFile(path) - if err != nil { - panic(errors.Wrap(err, "reading file")) - } - if err := json.Unmarshal(dat, destination); err != nil { - panic(errors.Wrap(err, "unmarshalling json")) - } -} - -// ReadFile reads file and returns the byte -func ReadFile(path string) []byte { - dat, err := ioutil.ReadFile(path) - if err != nil { - panic(errors.Wrap(err, "reading file")) - } - - return dat -} - // InitTestDB establishes connection pool with the test database specified by // the environment variable configuration and initalizes a new schema func InitTestDB() { @@ -304,19 +201,6 @@ func MakeReq(server *httptest.Server, method, url, data string) *http.Request { return req } -// AssertStatusCode asserts that the reponse's status code is equal to the -// expected -func AssertStatusCode(t *testing.T, res *http.Response, expected int, message string) { - if res.StatusCode != expected { - body, err := ioutil.ReadAll(res.Body) - if err != nil { - panic(errors.Wrap(err, "reading body")) - } - - t.Errorf("status code mismatch. %s: got %v want %v. Message was: '%s'", message, res.StatusCode, expected, string(body)) - } -} - // MustExec fails the test if the given database query has error func MustExec(t *testing.T, db *gorm.DB, message string) { if err := db.Error; err != nil { @@ -324,17 +208,6 @@ func MustExec(t *testing.T, db *gorm.DB, message string) { } } -// MustMarshalJSON marshalls the given interface into JSON. -// If there is any error, it fails the test. -func MustMarshalJSON(t *testing.T, v interface{}) []byte { - b, err := json.Marshal(v) - if err != nil { - t.Fatal("marshalling data") - } - - return b -} - // GetCookieByName returns a cookie with the given name func GetCookieByName(cookies []*http.Cookie, name string) *http.Cookie { var ret *http.Cookie diff --git a/scripts/license.sh b/scripts/license.sh index 6f62e52f..90655f79 100755 --- a/scripts/license.sh +++ b/scripts/license.sh @@ -54,19 +54,20 @@ agpl="/* Copyright (C) 2019 Monomax Software Pty Ltd * along with Dnote. If not, see . */" -cliPath="$GOPATH"/src/github.com/dnote/dnote/cli -cliFiles=$(find "$cliPath" -type f -name "*.go" ! -path "**/vendor/*") +pkgPath="$GOPATH"/src/github.com/dnote/dnote/pkg +serverPath="$GOPATH"/src/github.com/dnote/dnote/pkg/server -for file in $cliFiles; do +pkgFiles=$(find "$pkgPath" -type f -name "*.go" ! -path "**/vendor/*" ! -path "$serverPath/*") + +for file in $pkgFiles; do remove_notice "$file" add_notice "$file" "$gpl" done - serverPath="$GOPATH"/src/github.com/dnote/dnote/server - webPath="$GOPATH"/src/github.com/dnote/dnote/web - agplFiles=$(find "$serverPath" "$webPath" -type f \( -name "*.go" -o -name "*.js" -o -name "*.scss" \) ! -path "**/vendor/*" ! -path "**/node_modules/*") - - for file in $agplFiles; do - remove_notice "$file" - add_notice "$file" "$agpl" - done +webPath="$GOPATH"/src/github.com/dnote/dnote/web +agplFiles=$(find "$serverPath" "$webPath" -type f \( -name "*.go" -o -name "*.js" -o -name "*.scss" \) ! -path "**/vendor/*" ! -path "**/node_modules/*") + +for file in $agplFiles; do + remove_notice "$file" + add_notice "$file" "$agpl" +done