dnote/pkg/server/operations/notes_test.go
Sung Won Cho bd97209af8
Refactor to avoid global database variable (#313)
* Avoid global database

* Fix Twitter summary card

* Fix CLI test
2019-11-16 09:45:56 +08:00

374 lines
12 KiB
Go

/* Copyright (C) 2019 Monomax Software Pty Ltd
*
* This file is part of Dnote.
*
* Dnote is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Dnote is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Dnote. If not, see <https://www.gnu.org/licenses/>.
*/
package operations
import (
"fmt"
"testing"
"time"
"github.com/dnote/dnote/pkg/assert"
"github.com/dnote/dnote/pkg/clock"
"github.com/dnote/dnote/pkg/server/database"
"github.com/dnote/dnote/pkg/server/testutils"
"github.com/pkg/errors"
)
func TestCreateNote(t *testing.T) {
serverTime := time.Date(2017, time.March, 14, 21, 15, 0, 0, time.UTC)
mockClock := clock.NewMock()
mockClock.SetNow(serverTime)
ts1 := time.Date(2018, time.November, 12, 10, 11, 0, 0, time.UTC).UnixNano()
ts2 := time.Date(2018, time.November, 15, 0, 1, 10, 0, time.UTC).UnixNano()
testCases := []struct {
userUSN int
addedOn *int64
editedOn *int64
expectedUSN int
expectedAddedOn int64
expectedEditedOn int64
}{
{
userUSN: 8,
addedOn: nil,
editedOn: nil,
expectedUSN: 9,
expectedAddedOn: serverTime.UnixNano(),
expectedEditedOn: 0,
},
{
userUSN: 102229,
addedOn: &ts1,
editedOn: nil,
expectedUSN: 102230,
expectedAddedOn: ts1,
expectedEditedOn: 0,
},
{
userUSN: 8099,
addedOn: &ts1,
editedOn: &ts2,
expectedUSN: 8100,
expectedAddedOn: ts1,
expectedEditedOn: ts2,
},
}
for idx, tc := range testCases {
func() {
defer testutils.ClearData()
user := testutils.SetupUserData()
testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", tc.userUSN), fmt.Sprintf("preparing user max_usn for test case %d", idx))
anotherUser := testutils.SetupUserData()
testutils.MustExec(t, testutils.DB.Model(&anotherUser).Update("max_usn", 55), fmt.Sprintf("preparing user max_usn for test case %d", idx))
b1 := database.Book{UserID: user.ID, Label: "js", Deleted: false}
testutils.MustExec(t, testutils.DB.Save(&b1), fmt.Sprintf("preparing b1 for test case %d", idx))
tx := testutils.DB.Begin()
if _, err := CreateNote(testutils.DB, user, mockClock, b1.UUID, "note content", tc.addedOn, tc.editedOn, false); err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "deleting note"))
}
tx.Commit()
var bookCount, noteCount int
var noteRecord database.Note
var userRecord database.User
testutils.MustExec(t, testutils.DB.Model(&database.Book{}).Count(&bookCount), fmt.Sprintf("counting book for test case %d", idx))
testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(&noteCount), fmt.Sprintf("counting notes for test case %d", idx))
testutils.MustExec(t, testutils.DB.First(&noteRecord), fmt.Sprintf("finding note for test case %d", idx))
testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), fmt.Sprintf("finding user for test case %d", idx))
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")
assert.Equal(t, userRecord.MaxUSN, tc.expectedUSN, "user max_usn mismatch")
}()
}
}
func TestUpdateNote(t *testing.T) {
testCases := []struct {
userUSN int
}{
{
userUSN: 8,
},
{
userUSN: 102229,
},
{
userUSN: 8099,
},
}
for idx, tc := range testCases {
t.Run(fmt.Sprintf("test case %d", idx), func(t *testing.T) {
defer testutils.ClearData()
user := testutils.SetupUserData()
testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", tc.userUSN), "preparing user max_usn for test case")
anotherUser := testutils.SetupUserData()
testutils.MustExec(t, testutils.DB.Model(&anotherUser).Update("max_usn", 55), "preparing user max_usn for test case")
b1 := database.Book{UserID: user.ID, Label: "js", Deleted: false}
testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1 for test case")
note := database.Note{UserID: user.ID, Deleted: false, Body: "test content", BookUUID: b1.UUID}
testutils.MustExec(t, testutils.DB.Save(&note), "preparing note for test case")
c := clock.NewMock()
content := "updated test content"
public := true
tx := testutils.DB.Begin()
if _, err := UpdateNote(tx, user, c, note, &UpdateNoteParams{
Content: &content,
Public: &public,
}); err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "deleting note"))
}
tx.Commit()
var bookCount, noteCount int
var noteRecord database.Note
var userRecord database.User
testutils.MustExec(t, testutils.DB.Model(&database.Book{}).Count(&bookCount), "counting book for test case")
testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(&noteCount), "counting notes for test case")
testutils.MustExec(t, testutils.DB.First(&noteRecord), "finding note for test case")
testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), "finding user for test case")
expectedUSN := tc.userUSN + 1
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.Public, public, "note Public mismatch")
assert.Equal(t, noteRecord.Deleted, false, "note Deleted mismatch")
assert.Equal(t, noteRecord.USN, expectedUSN, "note USN mismatch")
assert.Equal(t, userRecord.MaxUSN, expectedUSN, "user MaxUSN mismatch")
})
}
}
func TestDeleteNote(t *testing.T) {
testCases := []struct {
userUSN int
expectedUSN int
}{
{
userUSN: 3,
expectedUSN: 4,
},
{
userUSN: 9787,
expectedUSN: 9788,
},
{
userUSN: 787,
expectedUSN: 788,
},
}
for idx, tc := range testCases {
func() {
defer testutils.ClearData()
user := testutils.SetupUserData()
testutils.MustExec(t, testutils.DB.Model(&user).Update("max_usn", tc.userUSN), fmt.Sprintf("preparing user max_usn for test case %d", idx))
anotherUser := testutils.SetupUserData()
testutils.MustExec(t, testutils.DB.Model(&anotherUser).Update("max_usn", 55), fmt.Sprintf("preparing user max_usn for test case %d", idx))
b1 := database.Book{UserID: user.ID, Label: "testBook"}
testutils.MustExec(t, testutils.DB.Save(&b1), fmt.Sprintf("preparing b1 for test case %d", idx))
note := database.Note{UserID: user.ID, Deleted: false, Body: "test content", BookUUID: b1.UUID}
testutils.MustExec(t, testutils.DB.Save(&note), fmt.Sprintf("preparing note for test case %d", idx))
tx := testutils.DB.Begin()
ret, err := DeleteNote(tx, user, note)
if err != nil {
tx.Rollback()
t.Fatal(errors.Wrap(err, "deleting note"))
}
tx.Commit()
var noteCount int
var noteRecord database.Note
var userRecord database.User
testutils.MustExec(t, testutils.DB.Model(&database.Note{}).Count(&noteCount), fmt.Sprintf("counting notes for test case %d", idx))
testutils.MustExec(t, testutils.DB.First(&noteRecord), fmt.Sprintf("finding note for test case %d", idx))
testutils.MustExec(t, testutils.DB.Where("id = ?", user.ID).First(&userRecord), fmt.Sprintf("finding user for test case %d", idx))
assert.Equal(t, noteCount, 1, "note count 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")
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")
}()
}
}
func TestGetNote(t *testing.T) {
user := testutils.SetupUserData()
anotherUser := testutils.SetupUserData()
defer testutils.ClearData()
b1 := database.Book{
UserID: user.ID,
Label: "js",
}
testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1")
privateNote := database.Note{
UserID: user.ID,
BookUUID: b1.UUID,
Body: "privateNote content",
Deleted: false,
Public: false,
}
testutils.MustExec(t, testutils.DB.Save(&privateNote), "preparing privateNote")
publicNote := database.Note{
UserID: user.ID,
BookUUID: b1.UUID,
Body: "privateNote content",
Deleted: false,
Public: true,
}
testutils.MustExec(t, testutils.DB.Save(&publicNote), "preparing privateNote")
var privateNoteRecord, publicNoteRecord database.Note
testutils.MustExec(t, testutils.DB.Where("uuid = ?", privateNote.UUID).Preload("Book").Preload("User").First(&privateNoteRecord), "finding privateNote")
testutils.MustExec(t, testutils.DB.Where("uuid = ?", publicNote.UUID).Preload("Book").Preload("User").First(&publicNoteRecord), "finding publicNote")
testCases := []struct {
name string
user database.User
note database.Note
expectedOK bool
expectedNote database.Note
}{
{
name: "owner accessing private note",
user: user,
note: privateNote,
expectedOK: true,
expectedNote: privateNoteRecord,
},
{
name: "non-owner accessing private note",
user: anotherUser,
note: privateNote,
expectedOK: false,
expectedNote: database.Note{},
},
{
name: "non-owner accessing public note",
user: anotherUser,
note: publicNote,
expectedOK: true,
expectedNote: publicNoteRecord,
},
{
name: "guest accessing private note",
user: database.User{},
note: privateNote,
expectedOK: false,
expectedNote: database.Note{},
},
{
name: "guest accessing public note",
user: database.User{},
note: publicNote,
expectedOK: true,
expectedNote: publicNoteRecord,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
note, ok, err := GetNote(testutils.DB, tc.note.UUID, tc.user)
if err != nil {
t.Fatal(errors.Wrap(err, "executing"))
}
assert.Equal(t, ok, tc.expectedOK, "ok mismatch")
assert.DeepEqual(t, note, tc.expectedNote, "note mismatch")
})
}
}
func TestGetNote_nonexistent(t *testing.T) {
user := testutils.SetupUserData()
defer testutils.ClearData()
b1 := database.Book{
UserID: user.ID,
Label: "js",
}
testutils.MustExec(t, testutils.DB.Save(&b1), "preparing b1")
n1UUID := "4fd19336-671e-4ff3-8f22-662b80e22edc"
n1 := database.Note{
UUID: n1UUID,
UserID: user.ID,
BookUUID: b1.UUID,
Body: "n1 content",
Deleted: false,
Public: false,
}
testutils.MustExec(t, testutils.DB.Save(&n1), "preparing n1")
nonexistentUUID := "4fd19336-671e-4ff3-8f22-662b80e22edd"
note, ok, err := GetNote(testutils.DB, nonexistentUUID, user)
if err != nil {
t.Fatal(errors.Wrap(err, "executing"))
}
assert.Equal(t, ok, false, "ok mismatch")
assert.DeepEqual(t, note, database.Note{}, "note mismatch")
}