refactor: remove duplicate write function in db
All checks were successful
/ test (pull_request) Successful in 40s
/ lint (pull_request) Successful in 42s

Remove the original 'Write' function from the db package and rename the
'WriteMany' function to 'Write' as it can already write one or many Bolt
items to the database.

Also update the test suites and add more coverage in the board package.
This commit is contained in:
Dan Anglin 2024-01-23 18:31:01 +00:00
parent 4e7eb77583
commit 5189ebe7bb
Signed by: dananglin
GPG key ID: 0C1D44CFBEE68638
9 changed files with 318 additions and 164 deletions

2
.gitignore vendored
View file

@ -2,3 +2,5 @@
/pelican
/*.pelican
/hack
/cover.out
/code-coverage.html

View file

@ -41,7 +41,7 @@ func Open(path string) (Board, error) {
boltItems[i] = &newStatusList[i]
}
if _, err := db.WriteMany(database, db.StatusBucket, boltItems); err != nil {
if _, err := db.Write(database, db.WriteModeCreate, db.StatusBucket, boltItems); err != nil {
return Board{}, fmt.Errorf("unable to save the default status list to the db. %w", err)
}
}
@ -147,7 +147,7 @@ func (b *Board) CreateStatus(args StatusArgs) error {
CardIds: nil,
}
if _, err := db.Write(b.db, db.StatusBucket, &status); err != nil {
if _, err := db.Write(b.db, db.WriteModeCreate, db.StatusBucket, []db.BoltItem{&status}); err != nil {
return fmt.Errorf("unable to write the status to the database; %w", err)
}
@ -175,7 +175,7 @@ func (b *Board) UpdateStatus(args UpdateStatusArgs) error {
status.Position = args.Position
}
if _, err := db.Write(b.db, db.StatusBucket, &status); err != nil {
if _, err := db.Write(b.db, db.WriteModeUpdate, db.StatusBucket, []db.BoltItem{&status}); err != nil {
return fmt.Errorf("unable to write the status to the db. %w", err)
}
@ -253,7 +253,7 @@ func (b *Board) MoveToStatus(args MoveToStatusArgs) error {
boltItems := []db.BoltItem{&currentStatus, &nextStatus}
if _, err := db.WriteMany(b.db, db.StatusBucket, boltItems); err != nil {
if _, err := db.Write(b.db, db.WriteModeUpdate, db.StatusBucket, boltItems); err != nil {
return fmt.Errorf("unable to update the statuses in the db. %w", err)
}
@ -278,32 +278,39 @@ func (b *Board) CreateCard(args CardArgs) (int, error) {
return 0, StatusListEmptyError{}
}
card := Card{
ID: -1,
Title: args.NewTitle,
Description: args.NewDescription,
Created: timestamp,
boltItems := []db.BoltItem{
&Card{
ID: -1,
Title: args.NewTitle,
Description: args.NewDescription,
Created: timestamp,
},
}
cardID, err := db.Write(b.db, db.CardBucket, &card)
cardIDs, err := db.Write(b.db, db.WriteModeCreate, db.CardBucket, boltItems)
if err != nil {
return 0, fmt.Errorf("unable to write card to the db. %w", err)
}
initialStatus := statusList[0]
initialStatus.AddCardID(cardID)
id, err := db.Write(b.db, db.StatusBucket, &initialStatus)
if err != nil {
return 0, fmt.Errorf("unable to write the %s status to the db. %w", initialStatus.Name, err)
if len(cardIDs) != 1 {
return 0, fmt.Errorf("unexpected number of card IDs returned after writing the card to the database; want: 1, got %d", len(cardIDs))
}
return id, nil
cardID := cardIDs[0]
statusInFirstPos := statusList[0]
statusInFirstPos.AddCardID(cardID)
_, err = db.Write(b.db, db.WriteModeUpdate, db.StatusBucket, []db.BoltItem{&statusInFirstPos})
if err != nil {
return 0, fmt.Errorf("unable to write the %s status to the db. %w", statusInFirstPos.Name, err)
}
return cardID, nil
}
// Card returns a Card value from the database.
// TODO: Handle edge case where the card does not exist in the db.
func (b *Board) Card(cardID int) (Card, error) {
data, err := db.Read(b.db, db.CardBucket, cardID)
if err != nil {
@ -328,7 +335,6 @@ func (b *Board) Card(cardID int) (Card, error) {
}
// CardList returns a list of Card values from the db.
// TODO: function needs testing.
func (b *Board) CardList(ids []int) ([]Card, error) {
data, err := db.ReadMany(b.db, db.CardBucket, ids)
if err != nil {
@ -374,8 +380,8 @@ func (b *Board) UpdateCard(args UpdateCardArgs) error {
card.Description = args.NewDescription
}
if _, err := db.Write(b.db, db.CardBucket, &card); err != nil {
return fmt.Errorf("unable to write card to the db. %w", err)
if _, err := db.Write(b.db, db.WriteModeUpdate, db.CardBucket, []db.BoltItem{&card}); err != nil {
return fmt.Errorf("unable to write the card to the database; %w", err)
}
return nil
@ -399,7 +405,7 @@ func (b *Board) DeleteCard(args DeleteCardArgs) error {
status.RemoveCardID(args.CardID)
if _, err := db.Write(b.db, db.StatusBucket, &status); err != nil {
if _, err := db.Write(b.db, db.WriteModeUpdate, db.StatusBucket, []db.BoltItem{&status}); err != nil {
return fmt.Errorf("unable to update the status in the database; %w", err)
}

View file

@ -5,6 +5,7 @@ import (
"os"
"path/filepath"
"reflect"
"sort"
"testing"
"time"
@ -12,7 +13,7 @@ import (
)
func TestCardLifecycle(t *testing.T) {
t.Log("Testing the lifecycle of a card.")
t.Log("Testing the lifecycle of a couple of cards...")
projectDir, err := projectRoot()
if err != nil {
@ -31,116 +32,204 @@ func TestCardLifecycle(t *testing.T) {
_ = kanban.Close()
}()
initialCardTitle := "A test card."
initialCardContent := "Ensure that this card is safely stored in the database."
expectedCardID := 1
expectedStatusID := 1
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 Card", testCreateCard(kanban, initialCardTitle, initialCardContent, expectedCardID, expectedStatusID))
t.Run("Test Create Cards", testCreateCards(kanban, cardArgs))
t.Run("Test Read Card", testReadCard(kanban, expectedCardID, initialCardTitle, initialCardContent, timestamp))
t.Logf("Now that we've created some cards, let's retrieve one from the database...")
modifiedCardTitle := "Test card updated."
modifiedCardContent1 := "Ensure that this card is safely updated in the database."
t.Run("Test Read One Card", testReadOneCard(kanban, 1, cardArgs[1], timestamp))
t.Run("Test Update Card", testUpdateCard(kanban, expectedCardID, modifiedCardTitle, modifiedCardContent1, timestamp))
t.Logf("Let us try retrieving multiple cards...")
modifiedCardContent2 := "Updated card content only."
subCardArgs := make(map[int]board.CardArgs)
subCardArgs[2] = cardArgs[2]
subCardArgs[3] = cardArgs[3]
t.Run("Test Update Card Content", testUpdateCardContent(kanban, expectedCardID, modifiedCardTitle, modifiedCardContent2, timestamp))
t.Run("Test Read Multiple Cards", testReadManyCards(kanban, subCardArgs))
t.Run("Test Card Delete", testDeleteCard(kanban, expectedCardID, expectedStatusID))
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 testCreateCard(kanban board.Board, title, content string, expectedCardID, expectedStatusID int) func(t *testing.T) {
func testCreateCards(kanban board.Board, args map[int]board.CardArgs) func(t *testing.T) {
return func(t *testing.T) {
t.Log("When the card is created and saved to the database.")
t.Log("When creating and saving the cards to the database.")
args := board.CardArgs{
NewTitle: title,
NewDescription: content,
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)
}
}
if _, err := kanban.CreateCard(args); 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("\t\tVerifying that the card's ID is in the expected status...")
t.Logf("\tVerifying that the card IDs are present the status in first position...")
status, err := kanban.Status(expectedStatusID)
statusList, err := kanban.StatusList()
if err != nil {
t.Fatalf("ERROR: Unable to read status '%d', %v", expectedStatusID, err)
t.Fatalf("ERROR: Unable to retrieve the list of statuses; %v", err)
}
numCardIDs := len(status.CardIds)
if numCardIDs != 1 {
t.Fatalf("ERROR: Unexpected number of cards in status '%d', want: %d, got %d.", expectedStatusID, 1, numCardIDs)
if len(statusList) == 0 {
t.Fatal("ERROR: No statuses were returned from the database.")
}
if expectedCardID != status.CardIds[0] {
t.Errorf("%s\tUnexpected card ID found in the default status, want: %d, got %d.", failure, expectedCardID, status.CardIds[0])
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 ID found in the default status, got %d.", success, status.CardIds[0])
t.Logf("%s\tExpected card IDs found in the expected status, got %v.", success, expectedStatus.CardIds)
}
}
}
func testReadCard(kanban board.Board, cardID int, wantTitle, wantDescription, wantTimestamp string) func(t *testing.T) {
func testReadOneCard(kanban board.Board, cardID int, cardArgs board.CardArgs, wantTimestamp string) func(t *testing.T) {
return func(t *testing.T) {
t.Log("When a card is read from the database.")
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: %s, got: %s.", failure, wantTitle, card.Title)
t.Errorf("%s\tUnexpected card title received, want: %q, got: %q.", failure, wantTitle, card.Title)
} else {
t.Logf("%s\tExpected card title received, got: %s.", success, card.Title)
t.Logf("%s\tExpected card title received, got: %q.", success, card.Title)
}
if card.Description != wantDescription {
t.Errorf("%s\tUnexpected card content received, want: %s, got: %s.", failure, wantDescription, card.Description)
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: %s.", success, card.Description)
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: %s, got %s.", failure, wantTimestamp, card.Created)
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: %s.", success, card.Created)
t.Logf("%s\tExpected timestamp received for the created card, got: %q.", success, card.Created)
}
}
}
func testUpdateCard(kanban board.Board, cardID int, newTitle, newContent, timestamp string) func(t *testing.T) {
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.")
args := board.UpdateCardArgs{
CardID: cardID,
CardArgs: board.CardArgs{
NewTitle: newTitle,
NewDescription: newContent,
},
}
if err := kanban.UpdateCard(args); err != nil {
if err := kanban.UpdateCard(modifiedCardArgs); err != nil {
t.Fatalf("ERROR: Unable to update the test card, %s", err)
}
got, err := kanban.Card(cardID)
got, err := kanban.Card(modifiedCardArgs.CardID)
if err != nil {
t.Fatalf("ERROR: Unable to read the modified test card, %s", err)
}
want := board.Card{
ID: cardID,
Title: newTitle,
Description: newContent,
ID: modifiedCardArgs.CardID,
Title: modifiedCardArgs.NewTitle,
Description: modifiedCardArgs.NewDescription,
Created: timestamp,
}
@ -152,31 +241,23 @@ func testUpdateCard(kanban board.Board, cardID int, newTitle, newContent, timest
}
}
func testUpdateCardContent(kanban board.Board, cardID int, expectedTitle, newContent, timestamp string) func(t *testing.T) {
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.")
args := board.UpdateCardArgs{
CardID: cardID,
CardArgs: board.CardArgs{
NewTitle: "",
NewDescription: newContent,
},
}
if err := kanban.UpdateCard(args); err != nil {
if err := kanban.UpdateCard(modifiedCardArgs); err != nil {
t.Fatalf("ERROR: Unable to update the test card, %s", err)
}
got, err := kanban.Card(cardID)
got, err := kanban.Card(modifiedCardArgs.CardID)
if err != nil {
t.Fatalf("ERROR: Unable to read the modified test card, %s", err)
}
want := board.Card{
ID: cardID,
ID: modifiedCardArgs.CardID,
Title: expectedTitle,
Description: newContent,
Description: expectedDescription,
Created: timestamp,
}
@ -188,7 +269,7 @@ func testUpdateCardContent(kanban board.Board, cardID int, expectedTitle, newCon
}
}
func testDeleteCard(kanban board.Board, cardID, statusID int) func(t *testing.T) {
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.")
@ -206,18 +287,18 @@ func testDeleteCard(kanban board.Board, cardID, statusID int) func(t *testing.T)
t.Logf("\tVerifying that the card is removed from the database...")
_, err := kanban.Card(cardID)
if err == nil {
switch {
case err == nil:
t.Errorf("%s\tDid not receive the expected error when attempting to read the deleted card.", failure)
} else {
if errors.Is(err, board.CardNotExistError{}) {
t.Errorf(
"%s\tDid not receive the expected board.CardNotExistError when attempting to retrieve the deleted card, instead got '%v'.",
failure,
err,
)
} else {
t.Logf("%s\tSuccessfully received board.CardNotExistError when attempting to retrieve the deleted card.", success)
}
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...")
@ -227,11 +308,13 @@ func testDeleteCard(kanban board.Board, cardID, statusID int) func(t *testing.T)
t.Fatalf("ERROR: Unable to read status '%d' from the database; %v", statusID, err)
}
numCardIDs := len(status.CardIds)
if numCardIDs != 0 {
t.Errorf("%s\tUnexpected non-empty list of card IDs in status '%d', got '%+v' card IDs.", failure, statusID, status.CardIds)
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\tThe card ID was successfully removed from the list of card in status '%d'.", success, statusID)
t.Logf("%s\tExpected list of card IDs found in status '%d'; got %v", success, statusID, status.CardIds)
}
}
}

View file

@ -0,0 +1,49 @@
package board_test
import (
"errors"
"testing"
"time"
"codeflow.dananglin.me.uk/apollo/pelican/internal/board"
)
func TestCardSetInvalidID(t *testing.T) {
card := board.Card{
ID: -1,
Title: "Title",
Description: "Description",
Created: time.Now().Format(time.DateTime),
}
err := card.SetID(-1000)
switch {
case err == nil:
t.Errorf("%s\tWanted an error for setting an invalid card ID; got 'nil' instead.", failure)
case errors.As(err, &board.InvalidIDError{}):
t.Logf("%s\tGot expected error after attempting to set an invalid card ID; got '%v'", success, err)
default:
t.Errorf("%s\tGot unexpected error after attempting to set an invalid card ID; got '%v'", failure, err)
}
}
func TestCardSetExistingID(t *testing.T) {
card := board.Card{
ID: 5,
Title: "Title",
Description: "Description",
Created: time.Now().Format(time.DateTime),
}
err := card.SetID(10)
switch {
case err == nil:
t.Errorf("%s\tWanted an error for setting a card ID that's already been set; got 'nil' instead.", failure)
case errors.As(err, &board.IDAlreadySetError{}):
t.Logf("%s\tGot expected error after attempting to set a card ID that's already been set; got '%v'", success, err)
default:
t.Errorf("%s\tGot unexpected error after attempting to set a card ID that's already been set; got '%v'", failure, err)
}
}

View file

@ -50,6 +50,9 @@ func TestStatusLifecycle(t *testing.T) {
t.Run("Test Create Status (Next)", testCreateStatus(kanban, statusNextName, 0))
t.Run("Test Read Status (Next)", testReadStatus(kanban, statusNextExpectedID, statusNextName, statusNextExpectedBoardPosition))
t.Logf("Let us see what happens when we try and receive a status that does not exist...")
t.Run("Test Read Non-Existent Status", testReadNonExistentStatus(kanban, 1000))
t.Logf("Let us now update the names of two of our statuses...")
t.Run("Test Status Update (Doing to In Progress)", testUpdateStatus(kanban, 2, 2, "In Progress"))
t.Run("Test Status Update (To Do to Backlog)", testUpdateStatus(kanban, 1, 1, "Backlog"))
@ -262,3 +265,20 @@ func testDeleteNonEmptyStatus(kanban board.Board, statusID int) func(t *testing.
}
}
}
func testReadNonExistentStatus(kanban board.Board, statusID int) func(t *testing.T) {
return func(t *testing.T) {
t.Log("When attempting to retrieving a non-existent status from the database.")
_, err := kanban.Status(statusID)
switch {
case err == nil:
t.Errorf("%s\tWanted an error for trying to retrieve a non-existent status, but got 'nil' instead.", failure)
case errors.As(err, &board.StatusNotExistError{}):
t.Logf("%s\tExpected error received after attempting to retrieve a non-existent status from the database; got '%v'", success, err)
default:
t.Errorf("%s\tUnexpected error received after attempting to retrieve a non-existent status from the database; got '%v'", failure, err)
}
}
}

View file

@ -17,6 +17,13 @@ const (
CardBucket string = "card"
)
type dbWriteMode int
const (
WriteModeCreate dbWriteMode = iota
WriteModeUpdate
)
type BoltItem interface {
SetID(int) error
GetID() int
@ -128,52 +135,10 @@ func ReadAll(db *bolt.DB, bucketName string) ([][]byte, error) {
return output, nil
}
// Write creates or updates a Bolt item to a specified bucket.
func Write(db *bolt.DB, bucketName string, item BoltItem) (int, error) {
bucketNameBytes := []byte(bucketName)
err := db.Update(func(tx *bolt.Tx) error {
var err error
bucket := tx.Bucket(bucketNameBytes)
if bucket == nil {
return bucketNotExistError{bucket: string(bucketNameBytes)}
}
if item.GetID() < 1 {
var id uint64
if id, err = bucket.NextSequence(); err != nil {
return fmt.Errorf("unable to generate an ID for the Bolt item; %w", err)
}
if err = item.SetID(int(id)); err != nil {
return fmt.Errorf("unable to set the generated ID to the Bolt item; %w", err)
}
}
buf := new(bytes.Buffer)
encoder := gob.NewEncoder(buf)
if err = encoder.Encode(item); err != nil {
return fmt.Errorf("unable to encode data, %w", err)
}
if err = bucket.Put([]byte(strconv.Itoa(item.GetID())), buf.Bytes()); err != nil {
return fmt.Errorf("unable to write the Bolt item to the bucket; %w", err)
}
return nil
})
if err != nil {
return 0, fmt.Errorf("error while saving the Bolt item to the database; %w", err)
}
return item.GetID(), nil
}
// WriteMany saves one or more Bolt items to the status bucket.
func WriteMany(database *bolt.DB, bucketName string, items []BoltItem) ([]int, error) {
// Write saves one or more Bolt items to the status bucket.
func Write(database *bolt.DB, writeMode dbWriteMode, bucketName string, items []BoltItem) ([]int, error) {
if len(items) == 0 {
return []int{}, nil
return nil, itemListEmptyError{}
}
ids := make([]int, len(items))
@ -190,7 +155,7 @@ func WriteMany(database *bolt.DB, bucketName string, items []BoltItem) ([]int, e
for ind, item := range items {
var err error
if item.GetID() < 1 {
if writeMode == WriteModeCreate && item.GetID() < 1 {
var id uint64
if id, err = bucket.NextSequence(); err != nil {
return fmt.Errorf("unable to generate ID, %w", err)

View file

@ -36,11 +36,11 @@ func TestWriteAndReadStatusList(t *testing.T) {
_ = database.Close()
}()
testWriteStatusList(t, database)
testCreateStatusList(t, database)
testReadStatusList(t, database)
}
func testWriteStatusList(t *testing.T, database *bolt.DB) {
func testCreateStatusList(t *testing.T, database *bolt.DB) {
t.Helper()
newStatusList := []board.Status{
@ -76,7 +76,7 @@ func testWriteStatusList(t *testing.T, database *bolt.DB) {
boltItems[i] = &newStatusList[i]
}
if _, err := db.WriteMany(database, db.StatusBucket, boltItems); err != nil {
if _, err := db.Write(database, db.WriteModeCreate, db.StatusBucket, boltItems); err != nil {
t.Fatalf("An error occurred whilst writing the initial status list to the database, %s", err)
}
}
@ -168,7 +168,7 @@ func TestReadAndWriteCards(t *testing.T) {
Description: "This task should be completed.",
}
singleCardID := testWriteOneCard(t, database, singleCard)
singleCardID := testCreateOneCard(t, database, singleCard)
testReadOneCard(t, database, singleCardID)
manyCards := []board.Card{
@ -189,19 +189,23 @@ func TestReadAndWriteCards(t *testing.T) {
},
}
manyCardIDs := testWriteManyCard(t, database, manyCards)
manyCardIDs := testCreateManyCard(t, database, manyCards)
testReadManyCards(t, database, manyCardIDs)
}
func testWriteOneCard(t *testing.T, database *bolt.DB, card board.Card) int {
func testCreateOneCard(t *testing.T, database *bolt.DB, card board.Card) int {
t.Helper()
cardID, err := db.Write(database, db.CardBucket, &card)
cardIDs, err := db.Write(database, db.WriteModeCreate, db.CardBucket, []db.BoltItem{&card})
if err != nil {
t.Fatalf("An error occurred whilst writing the card to the database, %s", err)
}
return cardID
if len(cardIDs) != 1 {
t.Fatalf("Unexpected number of card IDs returned from db.Write(); want: 1; got %d", len(cardIDs))
}
return cardIDs[0]
}
func testReadOneCard(t *testing.T, database *bolt.DB, cardID int) {
@ -235,7 +239,7 @@ func testReadOneCard(t *testing.T, database *bolt.DB, cardID int) {
}
}
func testWriteManyCard(t *testing.T, database *bolt.DB, cards []board.Card) []int {
func testCreateManyCard(t *testing.T, database *bolt.DB, cards []board.Card) []int {
t.Helper()
boltItems := make([]db.BoltItem, len(cards))
@ -244,7 +248,7 @@ func testWriteManyCard(t *testing.T, database *bolt.DB, cards []board.Card) []in
boltItems[i] = &cards[i]
}
ids, err := db.WriteMany(database, db.CardBucket, boltItems)
ids, err := db.Write(database, db.WriteModeCreate, db.CardBucket, boltItems)
if err != nil {
t.Fatalf("An error occurred whilst writing many cards to the database, %s", err)
}
@ -331,11 +335,17 @@ func TestDeleteOneCard(t *testing.T) {
Description: "",
}
cardID, err := db.Write(database, db.CardBucket, &card)
cardIDs, err := db.Write(database, db.WriteModeCreate, db.CardBucket, []db.BoltItem{&card})
if err != nil {
t.Fatalf("ERROR: Unable to create the card in the database, %v", err)
}
if len(cardIDs) != 1 {
t.Fatalf("Unexpected number of card IDs returned from db.Write(); want: 1; got %d", len(cardIDs))
}
cardID := cardIDs[0]
cards, err := db.ReadAll(database, db.CardBucket)
if err != nil {
t.Fatalf("ERROR: Unable to read the cards from the database, %v", err)

View file

@ -9,3 +9,9 @@ type bucketNotExistError struct {
func (e bucketNotExistError) Error() string {
return fmt.Sprintf("bucket %s does not exist", e.bucket)
}
type itemListEmptyError struct{}
func (e itemListEmptyError) Error() string {
return "the list of items is empty."
}

View file

@ -24,6 +24,7 @@ var Default = Build
// Test run the go tests
// To enable verbose mode set PELICAN_TEST_VERBOSE=1.
// To enable coverage mode set PELICAN_TEST_COVER=1.
// To produce a coverage report set PELICAN_TEST_COVER_REPORT=1.
func Test() error {
test := sh.RunCmd("go", "test")
@ -37,7 +38,19 @@ func Test() error {
args = append(args, "-cover")
}
return test(args...)
if os.Getenv("PELICAN_TEST_COVER_REPORT") == "1" {
args = append(args, "-coverprofile=cover.out")
}
if err := test(args...); err != nil {
return err
}
if os.Getenv("PELICAN_TEST_COVER_REPORT") == "1" {
return sh.Run("go", "tool", "cover", "-html=cover.out", "-o", "code-coverage.html")
}
return nil
}
// Lint runs golangci-lint against the code.