Dan Anglin
73547c49c6
Add a new Identity type with the required methods so that now the board types (Card, Status and soon Tag) can automatically satisfy the BoltItem interface.
320 lines
9.9 KiB
Go
320 lines
9.9 KiB
Go
package board_test
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
"time"
|
|
|
|
"codeflow.dananglin.me.uk/apollo/pelican/internal/board"
|
|
)
|
|
|
|
func TestCardLifecycle(t *testing.T) {
|
|
t.Log("Testing the lifecycle of a couple of cards...")
|
|
|
|
projectDir, err := projectRoot()
|
|
if err != nil {
|
|
t.Fatalf(err.Error())
|
|
}
|
|
|
|
testDBPath := filepath.Join(projectDir, "test", "databases", "Board_TestCardLifecycle.db")
|
|
os.Remove(testDBPath)
|
|
|
|
kanban, err := board.Open(testDBPath)
|
|
if err != nil {
|
|
t.Fatalf("Unable to open the test Kanban board, %s.", err)
|
|
}
|
|
|
|
defer func() {
|
|
_ = kanban.Close()
|
|
}()
|
|
|
|
t.Logf("We've opened a new board, let us create a couple of cards...")
|
|
|
|
cardArgs := map[int]board.CardArgs{
|
|
1: {
|
|
NewTitle: "Test Card 01",
|
|
NewDescription: "Ensure that the cards are safely stored in the database.",
|
|
},
|
|
2: {
|
|
NewTitle: "Test Card 02",
|
|
NewDescription: "Ensure that cards can be modified and that the modifications are saved in the database.",
|
|
},
|
|
3: {
|
|
NewTitle: "Test Card 03",
|
|
NewDescription: "Ensure that cards can be deleted from the database.",
|
|
},
|
|
}
|
|
|
|
timestamp := time.Now().Format(time.DateTime)
|
|
|
|
t.Run("Test Create Cards", testCreateCards(kanban, cardArgs))
|
|
|
|
t.Logf("Now that we've created some cards, let's retrieve one from the database...")
|
|
|
|
t.Run("Test Read One Card", testReadOneCard(kanban, 1, cardArgs[1], timestamp))
|
|
|
|
t.Logf("Let us try retrieving multiple cards...")
|
|
|
|
subCardArgs := make(map[int]board.CardArgs)
|
|
subCardArgs[2] = cardArgs[2]
|
|
subCardArgs[3] = cardArgs[3]
|
|
|
|
t.Run("Test Read Multiple Cards", testReadManyCards(kanban, subCardArgs))
|
|
|
|
t.Logf("Let's see what happens when trying to read a card that does not exist in the database...")
|
|
|
|
t.Run("Test Read Non-Existent Card", testReadNonExistentCard(kanban, 1000))
|
|
|
|
t.Logf("Let us try modifying the title and description of one of the cards...")
|
|
|
|
modifiedCardArgs := board.UpdateCardArgs{
|
|
CardID: 1,
|
|
CardArgs: board.CardArgs{
|
|
NewTitle: cardArgs[1].NewTitle + " (edited).",
|
|
NewDescription: "This description has been edited.",
|
|
},
|
|
}
|
|
|
|
t.Run("Test Update Card", testUpdateCard(kanban, modifiedCardArgs, timestamp))
|
|
|
|
t.Logf("Let us also try only modifying the description of a card...")
|
|
|
|
expectedCardContent := "Only the description is updated in this card."
|
|
|
|
modifiedCardArgs = board.UpdateCardArgs{
|
|
CardID: 2,
|
|
CardArgs: board.CardArgs{
|
|
NewTitle: "",
|
|
NewDescription: expectedCardContent,
|
|
},
|
|
}
|
|
|
|
expectedCardTitle := "Test Card 02"
|
|
|
|
t.Run("Test Update Card Content Only", testUpdateCardContent(kanban, modifiedCardArgs, expectedCardTitle, expectedCardContent, timestamp))
|
|
|
|
t.Logf("Let's now delete a card from the database...")
|
|
|
|
t.Run("Test Card Delete", testDeleteCard(kanban, 1, 1, []int{2, 3}))
|
|
}
|
|
|
|
func testCreateCards(kanban board.Board, args map[int]board.CardArgs) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
t.Log("When creating and saving the cards to the database.")
|
|
|
|
for i := 1; i <= len(args); i++ {
|
|
if _, err := kanban.CreateCard(args[i]); err != nil {
|
|
t.Fatalf("ERROR: Unable to create the test card, %s.", err)
|
|
}
|
|
}
|
|
|
|
t.Logf("%s\tAll cards have been saved to the database.", success)
|
|
|
|
t.Logf("\tVerifying that the card IDs are present the status in first position...")
|
|
|
|
statusList, err := kanban.StatusList()
|
|
if err != nil {
|
|
t.Fatalf("ERROR: Unable to retrieve the list of statuses; %v", err)
|
|
}
|
|
|
|
if len(statusList) == 0 {
|
|
t.Fatal("ERROR: No statuses were returned from the database.")
|
|
}
|
|
|
|
expectedStatus := statusList[0]
|
|
|
|
wantCardIDs := []int{1, 2, 3}
|
|
|
|
if !reflect.DeepEqual(expectedStatus.CardIds, wantCardIDs) {
|
|
t.Errorf("%s\tUnexpected card IDs found in the expected status, want: %v, got %v.", failure, wantCardIDs, expectedStatus.CardIds)
|
|
} else {
|
|
t.Logf("%s\tExpected card IDs found in the expected status, got %v.", success, expectedStatus.CardIds)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testReadOneCard(kanban board.Board, cardID int, cardArgs board.CardArgs, wantTimestamp string) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
t.Log("When reading a card from the database.")
|
|
|
|
card, err := kanban.Card(cardID)
|
|
if err != nil {
|
|
t.Fatalf("ERROR: Unable to read test card, %s.", err)
|
|
}
|
|
|
|
wantTitle := cardArgs.NewTitle
|
|
if card.Title != wantTitle {
|
|
t.Errorf("%s\tUnexpected card title received, want: %q, got: %q.", failure, wantTitle, card.Title)
|
|
} else {
|
|
t.Logf("%s\tExpected card title received, got: %q.", success, card.Title)
|
|
}
|
|
|
|
wantDescription := cardArgs.NewDescription
|
|
if card.Description != cardArgs.NewDescription {
|
|
t.Errorf("%s\tUnexpected card content received, want: %q, got: %q.", failure, wantDescription, card.Description)
|
|
} else {
|
|
t.Logf("%s\tExpected card content received, got: %q.", success, card.Description)
|
|
}
|
|
|
|
if card.Created != wantTimestamp {
|
|
t.Errorf("%s\tUnexpected timestamp received for the created card, want: %q, got %q.", failure, wantTimestamp, card.Created)
|
|
} else {
|
|
t.Logf("%s\tExpected timestamp received for the created card, got: %q.", success, card.Created)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testReadManyCards(kanban board.Board, cardArgs map[int]board.CardArgs) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
t.Log("When reading multiple cards from the database.")
|
|
|
|
var cardIDList []int
|
|
for key := range cardArgs {
|
|
cardIDList = append(cardIDList, key)
|
|
}
|
|
|
|
cards, err := kanban.CardList(cardIDList)
|
|
if err != nil {
|
|
t.Fatalf("ERROR: Unable to retrieve the list of cards from the database; %v", err)
|
|
}
|
|
|
|
for i := range cards {
|
|
cardID := cards[i].GetID()
|
|
wantTitle := cardArgs[cardID].NewTitle
|
|
gotTitle := cards[i].Title
|
|
|
|
if wantTitle != gotTitle {
|
|
t.Errorf("%s\tUnexpected card title received, want: %q, got: %q.", failure, wantTitle, gotTitle)
|
|
} else {
|
|
t.Logf("%s\tExpected card title received, got: %q.", success, gotTitle)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func testReadNonExistentCard(kanban board.Board, cardID int) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
t.Log("When attempting to retrieving a non-existent card from the database.")
|
|
|
|
_, err := kanban.Card(cardID)
|
|
|
|
switch {
|
|
case err == nil:
|
|
t.Errorf("%s\tWanted an error for retrieving a non-existent card but got 'nil' instead.", failure)
|
|
case errors.As(err, &board.CardNotExistError{}):
|
|
t.Logf("%s\tExpected error received after attempting to retrieve a non-existent card from the database; got '%v'", success, err)
|
|
default:
|
|
t.Errorf("%s\tUnexpected error received after attempting to retrieve a non-existent card from the database; got: '%v'", failure, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testUpdateCard(kanban board.Board, modifiedCardArgs board.UpdateCardArgs, timestamp string) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
t.Log("When a card is updated in the database.")
|
|
|
|
if err := kanban.UpdateCard(modifiedCardArgs); err != nil {
|
|
t.Fatalf("ERROR: Unable to update the test card, %s", err)
|
|
}
|
|
|
|
got, err := kanban.Card(modifiedCardArgs.CardID)
|
|
if err != nil {
|
|
t.Fatalf("ERROR: Unable to read the modified test card, %s", err)
|
|
}
|
|
|
|
want := board.Card{
|
|
Identity: board.Identity{ID: modifiedCardArgs.CardID},
|
|
Title: modifiedCardArgs.NewTitle,
|
|
Description: modifiedCardArgs.NewDescription,
|
|
Created: timestamp,
|
|
}
|
|
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("%s\tUnexpected card read from the database: want %+v, got %+v", failure, want, got)
|
|
} else {
|
|
t.Logf("%s\tExpected card read from the database: got %+v", success, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testUpdateCardContent(kanban board.Board, modifiedCardArgs board.UpdateCardArgs, expectedTitle, expectedDescription, timestamp string) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
t.Log("When (and only when) a card's content is updated in the database.")
|
|
|
|
if err := kanban.UpdateCard(modifiedCardArgs); err != nil {
|
|
t.Fatalf("ERROR: Unable to update the test card, %s", err)
|
|
}
|
|
|
|
got, err := kanban.Card(modifiedCardArgs.CardID)
|
|
if err != nil {
|
|
t.Fatalf("ERROR: Unable to read the modified test card, %s", err)
|
|
}
|
|
|
|
want := board.Card{
|
|
Identity: board.Identity{ID: modifiedCardArgs.CardID},
|
|
Title: expectedTitle,
|
|
Description: expectedDescription,
|
|
Created: timestamp,
|
|
}
|
|
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Errorf("%s\tUnexpected card read from the database, want: %+v, got: %+v", failure, want, got)
|
|
} else {
|
|
t.Logf("%s\tExpected card read from the database, got: %+v", success, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testDeleteCard(kanban board.Board, cardID, statusID int, expectedCardIDs []int) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
t.Log("When deleting a card from the database.")
|
|
|
|
args := board.DeleteCardArgs{
|
|
CardID: cardID,
|
|
StatusID: statusID,
|
|
}
|
|
|
|
if err := kanban.DeleteCard(args); err != nil {
|
|
t.Fatalf("ERROR: An error occurred when deleting the card from the database, %v", err)
|
|
} else {
|
|
t.Logf("%s\tNo errors occurred when deleting the card from the database.", success)
|
|
}
|
|
|
|
t.Logf("\tVerifying that the card is removed from the database...")
|
|
|
|
_, err := kanban.Card(cardID)
|
|
|
|
switch {
|
|
case err == nil:
|
|
t.Errorf("%s\tDid not receive the expected error when attempting to read the deleted card.", failure)
|
|
case errors.As(err, &board.CardNotExistError{}):
|
|
t.Logf("%s\tSuccessfully received board.CardNotExistError when attempting to retrieve the deleted card.", success)
|
|
default:
|
|
t.Errorf(
|
|
"%s\tDid not receive the expected error when attempting to retrieve the deleted card; got '%v'.",
|
|
failure,
|
|
err,
|
|
)
|
|
}
|
|
|
|
t.Logf("\tVerifying that the card's ID is removed from the status list...")
|
|
|
|
status, err := kanban.Status(statusID)
|
|
if err != nil {
|
|
t.Fatalf("ERROR: Unable to read status '%d' from the database; %v", statusID, err)
|
|
}
|
|
|
|
sort.Ints(expectedCardIDs)
|
|
sort.Ints(status.CardIds)
|
|
|
|
if !reflect.DeepEqual(status.CardIds, expectedCardIDs) {
|
|
t.Errorf("%s\tUnexpected list of card IDs found in status '%d'; want %v, got %v", failure, statusID, expectedCardIDs, status.CardIds)
|
|
} else {
|
|
t.Logf("%s\tExpected list of card IDs found in status '%d'; got %v", success, statusID, status.CardIds)
|
|
}
|
|
}
|
|
}
|