Additional test coverage for DB code. (#213)

* Test DB HTTP backup

* Test DB initialization

* Test CountTickets
This commit is contained in:
Jamie Holdstock 2020-12-26 20:42:50 +00:00 committed by GitHub
parent 2bee3203bd
commit 6474f0ea4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 176 additions and 4 deletions

View File

@ -6,19 +6,28 @@ package database
import ( import (
"context" "context"
"crypto/ed25519"
"io/ioutil"
"net/http"
"net/http/httptest"
"os" "os"
"strconv"
"sync" "sync"
"testing" "testing"
"time" "time"
) )
var ( const (
testDb = "test.db" testDb = "test.db"
backupDb = "test.db-backup" backupDb = "test.db-backup"
db *VspDatabase feeXPub = "feexpub"
maxVoteChangeRecords = 3 maxVoteChangeRecords = 3
) )
var (
db *VspDatabase
)
// TestDatabase runs all database tests. // TestDatabase runs all database tests.
func TestDatabase(t *testing.T) { func TestDatabase(t *testing.T) {
// Ensure we are starting with a clean environment. // Ensure we are starting with a clean environment.
@ -27,14 +36,17 @@ func TestDatabase(t *testing.T) {
// All sub-tests to run. // All sub-tests to run.
tests := map[string]func(*testing.T){ tests := map[string]func(*testing.T){
"testCreateNew": testCreateNew,
"testInsertNewTicket": testInsertNewTicket, "testInsertNewTicket": testInsertNewTicket,
"testGetTicketByHash": testGetTicketByHash, "testGetTicketByHash": testGetTicketByHash,
"testUpdateTicket": testUpdateTicket, "testUpdateTicket": testUpdateTicket,
"testTicketFeeExpired": testTicketFeeExpired, "testTicketFeeExpired": testTicketFeeExpired,
"testFilterTickets": testFilterTickets, "testFilterTickets": testFilterTickets,
"testCountTickets": testCountTickets,
"testAddressIndex": testAddressIndex, "testAddressIndex": testAddressIndex,
"testDeleteTicket": testDeleteTicket, "testDeleteTicket": testDeleteTicket,
"testVoteChangeRecords": testVoteChangeRecords, "testVoteChangeRecords": testVoteChangeRecords,
"testHTTPBackup": testHTTPBackup,
} }
for testName, test := range tests { for testName, test := range tests {
@ -42,7 +54,7 @@ func TestDatabase(t *testing.T) {
var err error var err error
var wg sync.WaitGroup var wg sync.WaitGroup
ctx, cancel := context.WithCancel(context.TODO()) ctx, cancel := context.WithCancel(context.TODO())
err = CreateNew(testDb, "feexpub") err = CreateNew(testDb, feeXPub)
if err != nil { if err != nil {
t.Fatalf("error creating test database: %v", err) t.Fatalf("error creating test database: %v", err)
} }
@ -63,4 +75,88 @@ func TestDatabase(t *testing.T) {
} }
} }
// TODO: Add tests for CountTickets. func testCreateNew(t *testing.T) {
// A newly created DB should contain a signing keypair.
priv, pub, err := db.KeyPair()
if err != nil {
t.Fatalf("error getting keypair: %v", err)
}
// Ensure keypair can be used for signing/verifying messages.
msg := []byte("msg")
sig := ed25519.Sign(priv, msg)
if !ed25519.Verify(pub, msg, sig) {
t.Fatalf("keypair from database could not be used to sign/verify a message")
}
// A newly created DB should have a cookie secret.
secret, err := db.GetCookieSecret()
if err != nil {
t.Fatalf("error getting cookie secret: %v", err)
}
if len(secret) != 32 {
t.Fatalf("expected a 32 byte cookie secret, got %d bytes", len(secret))
}
// A newly created DB should store the fee xpub it was initialized with.
retrievedXPub, err := db.GetFeeXPub()
if err != nil {
t.Fatalf("error getting fee xpub: %v", err)
}
if retrievedXPub != feeXPub {
t.Fatalf("expected fee xpub %v, got %v", feeXPub, retrievedXPub)
}
}
func testHTTPBackup(t *testing.T) {
// Capture the HTTP response written by the backup func.
rr := httptest.NewRecorder()
err := db.BackupDB(rr)
if err != nil {
t.Fatal(err)
}
// Check the HTTP status is OK.
if status := rr.Code; status != http.StatusOK {
t.Fatalf("wrong HTTP status code: expected %v, got %v",
http.StatusOK, status)
}
// Check HTTP headers.
header := "Content-Type"
expected := "application/octet-stream"
if actual := rr.Header().Get(header); actual != expected {
t.Errorf("wrong %s header: expected %s, got %s",
header, expected, actual)
}
header = "Content-Disposition"
expected = `attachment; filename="vspd.db"`
if actual := rr.Header().Get(header); actual != expected {
t.Errorf("wrong %s header: expected %s, got %s",
header, expected, actual)
}
header = "Content-Length"
cLength, err := strconv.Atoi(rr.Header().Get(header))
if err != nil {
t.Fatalf("could not convert %s to integer: %v", header, err)
}
if cLength <= 0 {
t.Fatalf("expected a %s greater than zero, got %d", header, cLength)
}
// Check reported length matches actual.
body, err := ioutil.ReadAll(rr.Result().Body)
if err != nil {
t.Fatalf("could not read http response body: %v", err)
}
if len(body) != cLength {
t.Fatalf("expected reported content-length to match actual body length. %v != %v",
cLength, len(body))
}
}

View File

@ -236,3 +236,79 @@ func testFilterTickets(t *testing.T) {
t.Fatal("expected to find 0 tickets") t.Fatal("expected to find 0 tickets")
} }
} }
func testCountTickets(t *testing.T) {
count := func(test string, expectedVoting, expectedVoted, expectedRevoked int64) {
voting, voted, revoked, err := db.CountTickets()
if err != nil {
t.Fatalf("error counting tickets: %v", err)
}
if voting != expectedVoting {
t.Fatalf("test %s: expected %d voting tickets, got %d",
test, expectedVoting, voting)
}
if voted != expectedVoted {
t.Fatalf("test %s: expected %d voted tickets, got %d",
test, expectedVoted, voted)
}
if revoked != expectedRevoked {
t.Fatalf("test %s: expected %d revoked tickets, got %d",
test, expectedRevoked, revoked)
}
}
// Initial counts should all be zero.
count("empty db", 0, 0, 0)
// Insert a ticket with non-confirmed fee into the database.
// This should not be counted.
ticket := exampleTicket()
ticket.FeeTxStatus = FeeReceieved
err := db.InsertNewTicket(ticket)
if err != nil {
t.Fatalf("error storing ticket in database: %v", err)
}
count("unconfirmed fee", 0, 0, 0)
// Insert a ticket with confirmed fee into the database.
// This should be counted.
ticket.Hash = ticket.Hash + "1"
ticket.FeeAddress = ticket.FeeAddress + "1"
ticket.FeeAddressIndex = ticket.FeeAddressIndex + 1
ticket.FeeTxStatus = FeeConfirmed
err = db.InsertNewTicket(ticket)
if err != nil {
t.Fatalf("error storing ticket in database: %v", err)
}
count("confirmed fee", 1, 0, 0)
// Insert a voted ticket into the database.
// This should be counted.
ticket.Hash = ticket.Hash + "1"
ticket.FeeAddress = ticket.FeeAddress + "1"
ticket.FeeAddressIndex = ticket.FeeAddressIndex + 1
ticket.FeeTxStatus = FeeConfirmed
ticket.Outcome = Voted
err = db.InsertNewTicket(ticket)
if err != nil {
t.Fatalf("error storing ticket in database: %v", err)
}
count("voted", 1, 1, 0)
// Insert a revoked ticket into the database.
ticket.Hash = ticket.Hash + "1"
ticket.FeeAddress = ticket.FeeAddress + "1"
ticket.FeeAddressIndex = ticket.FeeAddressIndex + 1
ticket.FeeTxStatus = FeeConfirmed
ticket.Outcome = Revoked
err = db.InsertNewTicket(ticket)
if err != nil {
t.Fatalf("error storing ticket in database: %v", err)
}
count("revoked", 1, 1, 1)
}