package db_test import ( "bytes" "encoding/gob" "os" "path/filepath" "reflect" "testing" "codeflow.dananglin.me.uk/apollo/pelican/internal/board" "codeflow.dananglin.me.uk/apollo/pelican/internal/db" bolt "go.etcd.io/bbolt" ) func TestWriteAndReadStatusList(t *testing.T) { t.Parallel() var database *bolt.DB var err error projectDir, err := projectRoot() if err != nil { t.Fatalf(err.Error()) } testDB := filepath.Join(projectDir, "test", "databases", "Database_TestWriteAndReadStatusList.db") os.Remove(testDB) if database, err = db.OpenDatabase(testDB); err != nil { t.Fatalf("An error occurred whilst opening the test database %s, %s.", testDB, err) } defer func() { _ = database.Close() }() testWriteStatusList(t, database) testReadStatusList(t, database) } func testWriteStatusList(t *testing.T, database *bolt.DB) { t.Helper() newStatusList := []board.Status{ { ID: -1, Name: "Backlog", CardIds: []int{1, 14, 9, 10}, Position: 1, }, { ID: -1, Name: "Next", CardIds: []int{2, 5, 12}, Position: 2, }, { ID: -1, Name: "In progress", CardIds: []int{3, 14}, Position: 3, }, { ID: -1, Name: "Finished!", CardIds: []int{4, 6, 7, 8, 11, 13}, Position: 4, }, } boltItems := make([]db.BoltItem, len(newStatusList)) for i := range newStatusList { boltItems[i] = &newStatusList[i] } if _, err := db.WriteMany(database, db.StatusBucket, boltItems); err != nil { t.Fatalf("An error occurred whilst writing the initial status list to the database, %s", err) } } func testReadStatusList(t *testing.T, database *bolt.DB) { t.Helper() data, err := db.ReadAll(database, db.StatusBucket) if err != nil { t.Fatalf("An error occurred whilst reading the modified status list from the database, %s", err) } got := make([]board.Status, len(data)) for ind, d := range data { buf := bytes.NewBuffer(d) decoder := gob.NewDecoder(buf) var status board.Status if err := decoder.Decode(&status); err != nil { t.Fatalf("An error occurred whilst decoding data, %s", err) } got[ind] = status } want := []board.Status{ { ID: 1, Name: "Backlog", CardIds: []int{1, 14, 9, 10}, Position: 1, }, { ID: 2, Name: "Next", CardIds: []int{2, 5, 12}, Position: 2, }, { ID: 3, Name: "In progress", CardIds: []int{3, 14}, Position: 3, }, { ID: 4, Name: "Finished!", CardIds: []int{4, 6, 7, 8, 11, 13}, Position: 4, }, } if !reflect.DeepEqual(got, want) { t.Errorf("Unexpected status list read from the database: got %+v, want %+v", got, want) } else { t.Logf("Expected status list read from the database: got %+v", got) } } func TestReadAndWriteCards(t *testing.T) { t.Parallel() var database *bolt.DB var err error projectDir, err := projectRoot() if err != nil { t.Fatalf(err.Error()) } testDB := filepath.Join(projectDir, "test", "databases", "Database_TestReadWriteCard.db") os.Remove(testDB) if database, err = db.OpenDatabase(testDB); err != nil { t.Fatalf("An error occurred whilst opening the test database %s, %s.", testDB, err) } defer func() { _ = database.Close() }() singleCard := board.Card{ ID: -1, Title: "A test task.", Description: "This task should be completed.", } singleCardID := testWriteOneCard(t, database, singleCard) testReadOneCard(t, database, singleCardID) manyCards := []board.Card{ { ID: -1, Title: "Test card A.", Description: "This is test card A.", }, { ID: -1, Title: "Test card B.", Description: "This is test card B.", }, { ID: -1, Title: "Test card C.", Description: "This is test card C.", }, } manyCardIDs := testWriteManyCard(t, database, manyCards) testReadManyCards(t, database, manyCardIDs) } func testWriteOneCard(t *testing.T, database *bolt.DB, card board.Card) int { t.Helper() cardID, err := db.Write(database, db.CardBucket, &card) if err != nil { t.Fatalf("An error occurred whilst writing the card to the database, %s", err) } return cardID } func testReadOneCard(t *testing.T, database *bolt.DB, cardID int) { t.Helper() data, err := db.Read(database, db.CardBucket, cardID) if err != nil { t.Fatalf("An error occurred whilst loading the modified from the database, %s", err) } var got board.Card buf := bytes.NewBuffer(data) decoder := gob.NewDecoder(buf) if err := decoder.Decode(&got); err != nil { t.Fatalf("Unable to decode data, %s", err) } want := board.Card{ ID: 1, Title: "A test task.", Description: "This task should be completed.", } if !reflect.DeepEqual(got, want) { t.Errorf("Unexpected card read from the database: got %+v, want %+v", got, want) } else { t.Logf("Expected card read from the database: got %+v", got) } } func testWriteManyCard(t *testing.T, database *bolt.DB, cards []board.Card) []int { t.Helper() boltItems := make([]db.BoltItem, len(cards)) for i := range cards { boltItems[i] = &cards[i] } ids, err := db.WriteMany(database, db.CardBucket, boltItems) if err != nil { t.Fatalf("An error occurred whilst writing many cards to the database, %s", err) } return ids } func testReadManyCards(t *testing.T, database *bolt.DB, cardIDs []int) { t.Helper() data, err := db.ReadMany(database, db.CardBucket, cardIDs) if err != nil { t.Fatalf("An error occurred whilst reading the data from the database, %s", err) } got := make([]board.Card, len(data)) for i, d := range data { buf := bytes.NewBuffer(d) decoder := gob.NewDecoder(buf) var c board.Card if err := decoder.Decode(&c); err != nil { t.Fatalf("An error occurred whilst decoding data, %s", err) } got[i] = c } want := []board.Card{ { ID: 2, Title: "Test card A.", Description: "This is test card A.", }, { ID: 3, Title: "Test card B.", Description: "This is test card B.", }, { ID: 4, Title: "Test card C.", Description: "This is test card C.", }, } if !reflect.DeepEqual(got, want) { t.Errorf("Unexpected list of cards read from the database: got %+v, want %+v", got, want) } else { t.Logf("Expected list of cards read from the database: got %+v", got) } } func TestDeleteOneCard(t *testing.T) { t.Parallel() var database *bolt.DB var err error projectDir, err := projectRoot() if err != nil { t.Fatalf(err.Error()) } testDB := filepath.Join(projectDir, "test", "databases", "Database_TestDeleteOneCard.db") os.Remove(testDB) if database, err = db.OpenDatabase(testDB); err != nil { t.Fatalf("An error occurred whilst opening the test database %s, %s.", testDB, err) } defer func() { _ = database.Close() }() // Create one card, get card ID. card := board.Card{ ID: -1, Title: "Test card", Description: "", } cardID, err := db.Write(database, db.CardBucket, &card) if err != nil { t.Fatalf("ERROR: Unable to create the card in the database, %v", err) } cards, err := db.ReadAll(database, db.CardBucket) if err != nil { t.Fatalf("ERROR: Unable to read the cards from the database, %v", err) } numCards := len(cards) if numCards != 1 { t.Fatalf("ERROR: Unexpected number of cards returned from the card bucket; want 1; got %d", numCards) } if err := db.Delete(database, db.CardBucket, cardID); err != nil { t.Fatalf("ERROR: Unable to delete the card from the database, %v", err) } // Get all cards, expect length = 0; error if not 0 cards, err = db.ReadAll(database, db.CardBucket) if err != nil { t.Fatalf("ERROR: Unable to read the cards from the database, %v", err) } numCards = len(cards) if numCards != 0 { t.Errorf("%s\tUnexpected number of cards returned from the card bucket; want 0; got %d", failure, numCards) } else { t.Logf("%s\tThe card was successfully deleted from the database.", success) } }