2023-05-06 12:49:40 +01:00
|
|
|
package board_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"reflect"
|
2024-01-23 18:31:01 +00:00
|
|
|
"sort"
|
2023-05-06 12:49:40 +01:00
|
|
|
"testing"
|
2023-12-12 14:42:45 +00:00
|
|
|
"time"
|
2023-05-06 12:49:40 +01:00
|
|
|
|
|
|
|
"codeflow.dananglin.me.uk/apollo/pelican/internal/board"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestCardLifecycle(t *testing.T) {
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Log("Testing the lifecycle of a couple of cards...")
|
2023-05-06 12:49:40 +01:00
|
|
|
|
|
|
|
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()
|
|
|
|
}()
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
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.",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2024-01-10 12:12:54 +00:00
|
|
|
timestamp := time.Now().Format(time.DateTime)
|
2023-05-06 12:49:40 +01:00
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
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))
|
2023-05-06 12:49:40 +01:00
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Logf("Let's see what happens when trying to read a card that does not exist in the database...")
|
2023-05-06 12:49:40 +01:00
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Run("Test Read Non-Existent Card", testReadNonExistentCard(kanban, 1000))
|
2023-05-06 12:49:40 +01:00
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Logf("Let us try modifying the title and description of one of the cards...")
|
2023-05-06 12:49:40 +01:00
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
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,
|
|
|
|
},
|
|
|
|
}
|
2023-05-06 12:49:40 +01:00
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
expectedCardTitle := "Test Card 02"
|
2023-05-06 12:49:40 +01:00
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
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}))
|
2023-05-06 12:49:40 +01:00
|
|
|
}
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
func testCreateCards(kanban board.Board, args map[int]board.CardArgs) func(t *testing.T) {
|
2023-05-06 12:49:40 +01:00
|
|
|
return func(t *testing.T) {
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Log("When creating and saving the cards to the database.")
|
2023-05-06 12:49:40 +01:00
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
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)
|
|
|
|
}
|
2023-05-06 12:49:40 +01:00
|
|
|
}
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Logf("%s\tAll cards have been saved to the database.", success)
|
2023-05-06 12:49:40 +01:00
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Logf("\tVerifying that the card IDs are present the status in first position...")
|
2023-05-06 12:49:40 +01:00
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
statusList, err := kanban.StatusList()
|
2023-05-06 12:49:40 +01:00
|
|
|
if err != nil {
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Fatalf("ERROR: Unable to retrieve the list of statuses; %v", err)
|
2023-05-06 12:49:40 +01:00
|
|
|
}
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
if len(statusList) == 0 {
|
|
|
|
t.Fatal("ERROR: No statuses were returned from the database.")
|
2023-05-06 12:49:40 +01:00
|
|
|
}
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
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)
|
2023-05-06 12:49:40 +01:00
|
|
|
} else {
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Logf("%s\tExpected card IDs found in the expected status, got %v.", success, expectedStatus.CardIds)
|
2023-05-06 12:49:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
func testReadOneCard(kanban board.Board, cardID int, cardArgs board.CardArgs, wantTimestamp string) func(t *testing.T) {
|
2023-05-06 12:49:40 +01:00
|
|
|
return func(t *testing.T) {
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Log("When reading a card from the database.")
|
2023-05-06 12:49:40 +01:00
|
|
|
|
|
|
|
card, err := kanban.Card(cardID)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("ERROR: Unable to read test card, %s.", err)
|
|
|
|
}
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
wantTitle := cardArgs.NewTitle
|
2023-05-06 12:49:40 +01:00
|
|
|
if card.Title != wantTitle {
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Errorf("%s\tUnexpected card title received, want: %q, got: %q.", failure, wantTitle, card.Title)
|
2023-05-06 12:49:40 +01:00
|
|
|
} else {
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Logf("%s\tExpected card title received, got: %q.", success, card.Title)
|
2023-05-06 12:49:40 +01:00
|
|
|
}
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
wantDescription := cardArgs.NewDescription
|
|
|
|
if card.Description != cardArgs.NewDescription {
|
|
|
|
t.Errorf("%s\tUnexpected card content received, want: %q, got: %q.", failure, wantDescription, card.Description)
|
2023-05-06 12:49:40 +01:00
|
|
|
} else {
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Logf("%s\tExpected card content received, got: %q.", success, card.Description)
|
2023-05-06 12:49:40 +01:00
|
|
|
}
|
2023-12-12 14:42:45 +00:00
|
|
|
|
|
|
|
if card.Created != wantTimestamp {
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Errorf("%s\tUnexpected timestamp received for the created card, want: %q, got %q.", failure, wantTimestamp, card.Created)
|
2023-12-12 14:42:45 +00:00
|
|
|
} else {
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Logf("%s\tExpected timestamp received for the created card, got: %q.", success, card.Created)
|
2023-12-12 14:42:45 +00:00
|
|
|
}
|
2023-05-06 12:49:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
func testReadManyCards(kanban board.Board, cardArgs map[int]board.CardArgs) func(t *testing.T) {
|
2023-05-06 12:49:40 +01:00
|
|
|
return func(t *testing.T) {
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Log("When reading multiple cards from the database.")
|
|
|
|
|
|
|
|
var cardIDList []int
|
|
|
|
for key := range cardArgs {
|
|
|
|
cardIDList = append(cardIDList, key)
|
|
|
|
}
|
2023-05-06 12:49:40 +01:00
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
cards, err := kanban.CardList(cardIDList)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("ERROR: Unable to retrieve the list of cards from the database; %v", err)
|
2023-05-06 12:49:40 +01:00
|
|
|
}
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
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 {
|
2023-05-06 12:49:40 +01:00
|
|
|
t.Fatalf("ERROR: Unable to update the test card, %s", err)
|
|
|
|
}
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
got, err := kanban.Card(modifiedCardArgs.CardID)
|
2023-05-06 12:49:40 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("ERROR: Unable to read the modified test card, %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
want := board.Card{
|
2024-01-23 19:42:35 +00:00
|
|
|
Identity: board.Identity{ID: modifiedCardArgs.CardID},
|
2024-01-23 18:31:01 +00:00
|
|
|
Title: modifiedCardArgs.NewTitle,
|
|
|
|
Description: modifiedCardArgs.NewDescription,
|
2024-01-14 13:46:42 +00:00
|
|
|
Created: timestamp,
|
2023-05-06 12:49:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
func testUpdateCardContent(kanban board.Board, modifiedCardArgs board.UpdateCardArgs, expectedTitle, expectedDescription, timestamp string) func(t *testing.T) {
|
2023-05-06 12:49:40 +01:00
|
|
|
return func(t *testing.T) {
|
|
|
|
t.Log("When (and only when) a card's content is updated in the database.")
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
if err := kanban.UpdateCard(modifiedCardArgs); err != nil {
|
2023-05-06 12:49:40 +01:00
|
|
|
t.Fatalf("ERROR: Unable to update the test card, %s", err)
|
|
|
|
}
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
got, err := kanban.Card(modifiedCardArgs.CardID)
|
2023-05-06 12:49:40 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("ERROR: Unable to read the modified test card, %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
want := board.Card{
|
2024-01-23 19:42:35 +00:00
|
|
|
Identity: board.Identity{ID: modifiedCardArgs.CardID},
|
2024-01-14 13:46:42 +00:00
|
|
|
Title: expectedTitle,
|
2024-01-23 18:31:01 +00:00
|
|
|
Description: expectedDescription,
|
2024-01-14 13:46:42 +00:00
|
|
|
Created: timestamp,
|
2023-05-06 12:49:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
func testDeleteCard(kanban board.Board, cardID, statusID int, expectedCardIDs []int) func(t *testing.T) {
|
2023-05-06 12:49:40 +01:00
|
|
|
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)
|
2024-01-23 18:31:01 +00:00
|
|
|
|
|
|
|
switch {
|
|
|
|
case err == nil:
|
2023-05-06 12:49:40 +01:00
|
|
|
t.Errorf("%s\tDid not receive the expected error when attempting to read the deleted card.", failure)
|
2024-01-23 18:31:01 +00:00
|
|
|
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,
|
|
|
|
)
|
2023-05-06 12:49:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2024-01-23 18:31:01 +00:00
|
|
|
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)
|
2023-05-06 12:49:40 +01:00
|
|
|
} else {
|
2024-01-23 18:31:01 +00:00
|
|
|
t.Logf("%s\tExpected list of card IDs found in status '%d'; got %v", success, statusID, status.CardIds)
|
2023-05-06 12:49:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|