feat: move a card between statuses #3
6 changed files with 134 additions and 34 deletions
35
.golangci.yaml
Normal file
35
.golangci.yaml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
---
|
||||||
|
run:
|
||||||
|
concurrency: 2
|
||||||
|
timeout: 1m
|
||||||
|
issues-exit-code: 1
|
||||||
|
tests: true
|
||||||
|
|
||||||
|
output:
|
||||||
|
format: colored-line-number
|
||||||
|
print-issues-lines: true
|
||||||
|
print-linter-name: true
|
||||||
|
uniq-by-line: true
|
||||||
|
sort-results: true
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
exhaustivestruct:
|
||||||
|
struct-patterns:
|
||||||
|
- 'forge.dananglin.me.uk/code/dananglin/pelican.Status'
|
||||||
|
- 'forge.dananglin.me.uk/code/dananglin/pelican.Card'
|
||||||
|
lll:
|
||||||
|
line-length: 140
|
||||||
|
testpackage:
|
||||||
|
skip-regexp: (internal)_test\.go
|
||||||
|
|
||||||
|
linters:
|
||||||
|
enable-all: true
|
||||||
|
disable:
|
||||||
|
- gomnd
|
||||||
|
fast: false
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-rules:
|
||||||
|
- path: db_internal_test.go
|
||||||
|
linters:
|
||||||
|
- funlen
|
67
db.go
67
db.go
|
@ -19,7 +19,11 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func mkDataDir(dir string) error {
|
func mkDataDir(dir string) error {
|
||||||
return os.MkdirAll(dir, 0700)
|
if err := os.MkdirAll(dir, 0o700); err != nil {
|
||||||
|
return fmt.Errorf("error while making directory %s, %w", dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// dbPath returns the path to the database file. If a path is given then that is returned. Otherwise the default path is returned.
|
// dbPath returns the path to the database file. If a path is given then that is returned. Otherwise the default path is returned.
|
||||||
|
@ -32,6 +36,7 @@ func mkDataDir(dir string) error {
|
||||||
func dbPath(path string) (string, error) {
|
func dbPath(path string) (string, error) {
|
||||||
if len(path) > 0 {
|
if len(path) > 0 {
|
||||||
filepath.Dir(path)
|
filepath.Dir(path)
|
||||||
|
|
||||||
return path, mkDataDir(filepath.Dir(path))
|
return path, mkDataDir(filepath.Dir(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,14 +58,13 @@ func dbPath(path string) (string, error) {
|
||||||
dataDir = filepath.Join(os.Getenv("HOME"), ".pelican")
|
dataDir = filepath.Join(os.Getenv("HOME"), ".pelican")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(dataDir, 0700); err != nil {
|
if err := os.MkdirAll(dataDir, 0o700); err != nil {
|
||||||
return "", fmt.Errorf("unable to make directory %s, %w", dataDir, err)
|
return "", fmt.Errorf("unable to make directory %s, %w", dataDir, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
path = filepath.Join(dataDir, dbFilename)
|
path = filepath.Join(dataDir, dbFilename)
|
||||||
|
|
||||||
return path, mkDataDir(dataDir)
|
return path, mkDataDir(dataDir)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// openDatabase opens the database, at a given path, for reading and writing. If the file does not exist it will be created.
|
// openDatabase opens the database, at a given path, for reading and writing. If the file does not exist it will be created.
|
||||||
|
@ -69,7 +73,7 @@ func openDatabase(path string) (*bolt.DB, error) {
|
||||||
Timeout: 1 * time.Second,
|
Timeout: 1 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := bolt.Open(path, 0600, &opts)
|
db, err := bolt.Open(path, 0o600, &opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to open database at %s, %w", path, err)
|
return nil, fmt.Errorf("unable to open database at %s, %w", path, err)
|
||||||
}
|
}
|
||||||
|
@ -81,14 +85,20 @@ func openDatabase(path string) (*bolt.DB, error) {
|
||||||
func ensureBuckets(db *bolt.DB) error {
|
func ensureBuckets(db *bolt.DB) error {
|
||||||
buckets := []string{statusBucket, cardBucket}
|
buckets := []string{statusBucket, cardBucket}
|
||||||
|
|
||||||
return db.Update(func(tx *bolt.Tx) error {
|
err := db.Update(func(tx *bolt.Tx) error {
|
||||||
for _, v := range buckets {
|
for _, v := range buckets {
|
||||||
if _, err := tx.CreateBucketIfNotExists([]byte(v)); err != nil {
|
if _, err := tx.CreateBucketIfNotExists([]byte(v)); err != nil {
|
||||||
return fmt.Errorf("unable to ensure that %s bucket is created in the database, %w", v, err)
|
return fmt.Errorf("unable to ensure that %s bucket is created in the database, %w", v, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while ensuring buckets exist in the database, %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveStatuses saves one or more statuses to the status bucket.
|
// saveStatuses saves one or more statuses to the status bucket.
|
||||||
|
@ -99,22 +109,22 @@ func saveStatuses(db *bolt.DB, statuses []Status) error {
|
||||||
|
|
||||||
bucket := []byte(statusBucket)
|
bucket := []byte(statusBucket)
|
||||||
|
|
||||||
return db.Update(func(tx *bolt.Tx) error {
|
err := db.Update(func(tx *bolt.Tx) error {
|
||||||
b := tx.Bucket(bucket)
|
b := tx.Bucket(bucket)
|
||||||
|
|
||||||
if b == nil {
|
if b == nil {
|
||||||
return fmt.Errorf("bucket %s does not exist", bucket)
|
return bucketNotExistError{bucket: string(bucket)}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range statuses {
|
for _, v := range statuses {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if v.Id == 0 {
|
if v.ID < 1 {
|
||||||
var id uint64
|
var id uint64
|
||||||
if id, err = b.NextSequence(); err != nil {
|
if id, err = b.NextSequence(); err != nil {
|
||||||
return fmt.Errorf("unable to generate ID, %w", err)
|
return fmt.Errorf("unable to generate ID, %w", err)
|
||||||
}
|
}
|
||||||
v.Id = int(id)
|
v.ID = int(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
@ -123,25 +133,31 @@ func saveStatuses(db *bolt.DB, statuses []Status) error {
|
||||||
return fmt.Errorf("unable to encode data, %w", err)
|
return fmt.Errorf("unable to encode data, %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = b.Put([]byte(strconv.Itoa(v.Id)), buf.Bytes()); err != nil {
|
if err = b.Put([]byte(strconv.Itoa(v.ID)), buf.Bytes()); err != nil {
|
||||||
return fmt.Errorf("unable to add the status to the bucket, %w", err)
|
return fmt.Errorf("unable to add the status to the bucket, %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error while saving the statuses to the database, %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadAllStatuses retrieves all the statuses from the status bucket.
|
// loadAllStatuses retrieves all the statuses from the status bucket.
|
||||||
func loadAllStatuses(db *bolt.DB) ([]Status, error) {
|
func loadAllStatuses(db *bolt.DB) ([]Status, error) {
|
||||||
var statuses []Status
|
var statuses []Status
|
||||||
|
|
||||||
bucket := []byte(statusBucket)
|
bucket := []byte(statusBucket)
|
||||||
|
|
||||||
err := db.View(func(tx *bolt.Tx) error {
|
err := db.View(func(tx *bolt.Tx) error {
|
||||||
b := tx.Bucket(bucket)
|
b := tx.Bucket(bucket)
|
||||||
|
|
||||||
if b == nil {
|
if b == nil {
|
||||||
return fmt.Errorf("bucket %s does not exist", bucket)
|
return bucketNotExistError{bucket: string(bucket)}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := b.ForEach(func(_, v []byte) error {
|
if err := b.ForEach(func(_, v []byte) error {
|
||||||
|
@ -156,12 +172,16 @@ func loadAllStatuses(db *bolt.DB) ([]Status, error) {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return fmt.Errorf("unable to load status, %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return statuses, fmt.Errorf("error while loading statuses from the database, %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return statuses, err
|
return statuses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// saveCard writes a card to the card bucket in BoltDB.
|
// saveCard writes a card to the card bucket in BoltDB.
|
||||||
|
@ -173,15 +193,15 @@ func saveCard(db *bolt.DB, card Card) (int, error) {
|
||||||
b := tx.Bucket(bucket)
|
b := tx.Bucket(bucket)
|
||||||
|
|
||||||
if b == nil {
|
if b == nil {
|
||||||
return fmt.Errorf("bucket %s does not exist", bucket)
|
return bucketNotExistError{bucket: string(bucket)}
|
||||||
}
|
}
|
||||||
|
|
||||||
if card.Id == 0 {
|
if card.ID < 1 {
|
||||||
var id uint64
|
var id uint64
|
||||||
if id, err = b.NextSequence(); err != nil {
|
if id, err = b.NextSequence(); err != nil {
|
||||||
return fmt.Errorf("unable to generate an ID for the card, %w", err)
|
return fmt.Errorf("unable to generate an ID for the card, %w", err)
|
||||||
}
|
}
|
||||||
card.Id = int(id)
|
card.ID = int(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
@ -190,26 +210,30 @@ func saveCard(db *bolt.DB, card Card) (int, error) {
|
||||||
return fmt.Errorf("unable to encode data, %w", err)
|
return fmt.Errorf("unable to encode data, %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = b.Put([]byte(strconv.Itoa(card.Id)), buf.Bytes()); err != nil {
|
if err = b.Put([]byte(strconv.Itoa(card.ID)), buf.Bytes()); err != nil {
|
||||||
return fmt.Errorf("unable to write the card to the bucket, %w", err)
|
return fmt.Errorf("unable to write the card to the bucket, %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return card.ID, fmt.Errorf("error while saving the card to the database, %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return card.Id, err
|
return card.ID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadCard retrieves a card from the cards bucket in BoltDB.
|
// loadCard retrieves a card from the cards bucket in BoltDB.
|
||||||
func loadCard(db *bolt.DB, id int) (Card, error) {
|
func loadCard(db *bolt.DB, id int) (Card, error) {
|
||||||
var card Card
|
var card Card
|
||||||
|
|
||||||
bucket := []byte(cardBucket)
|
bucket := []byte(cardBucket)
|
||||||
|
|
||||||
err := db.View(func(tx *bolt.Tx) error {
|
err := db.View(func(tx *bolt.Tx) error {
|
||||||
b := tx.Bucket(bucket)
|
b := tx.Bucket(bucket)
|
||||||
|
|
||||||
if b == nil {
|
if b == nil {
|
||||||
return fmt.Errorf("bucket %s does not exist", bucket)
|
return bucketNotExistError{bucket: string(bucket)}
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := bytes.NewBuffer(b.Get([]byte(strconv.Itoa(id))))
|
buf := bytes.NewBuffer(b.Get([]byte(strconv.Itoa(id))))
|
||||||
|
@ -221,6 +245,9 @@ func loadCard(db *bolt.DB, id int) (Card, error) {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return card, fmt.Errorf("error while loading the card from the database, %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return card, err
|
return card, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDbCustomPath(t *testing.T) {
|
func TestDbCustomPath(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("An error occurred whilst getting the current directory, %s", err)
|
t.Fatalf("An error occurred whilst getting the current directory, %s", err)
|
||||||
|
@ -35,6 +37,8 @@ func TestDbCustomPath(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDbDefaultPath(t *testing.T) {
|
func TestDbDefaultPath(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
cwd, err := os.Getwd()
|
cwd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("An error occurred whilst getting the current directory, %s", err)
|
t.Fatalf("An error occurred whilst getting the current directory, %s", err)
|
||||||
|
@ -70,7 +74,10 @@ func TestDbDefaultPath(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEnsureBuckets(t *testing.T) {
|
func TestEnsureBuckets(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
var db *bolt.DB
|
var db *bolt.DB
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
testDB := "test/databases/TestEnsureBuckets.db"
|
testDB := "test/databases/TestEnsureBuckets.db"
|
||||||
|
@ -93,9 +100,10 @@ func TestEnsureBuckets(t *testing.T) {
|
||||||
if err = db.View(func(tx *bolt.Tx) error {
|
if err = db.View(func(tx *bolt.Tx) error {
|
||||||
for _, b := range expectedBuckets {
|
for _, b := range expectedBuckets {
|
||||||
if bucket := tx.Bucket([]byte(b)); bucket == nil {
|
if bucket := tx.Bucket([]byte(b)); bucket == nil {
|
||||||
return fmt.Errorf("bucket %s does not exist in the database", b)
|
return bucketNotExistError{bucket: b}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
t.Fatalf("An error occurred whilst checking for the buckets, %s.", err)
|
t.Fatalf("An error occurred whilst checking for the buckets, %s.", err)
|
||||||
|
@ -103,7 +111,10 @@ func TestEnsureBuckets(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadAndWriteStatuses(t *testing.T) {
|
func TestReadAndWriteStatuses(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
var db *bolt.DB
|
var db *bolt.DB
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
testDB := "test/databases/TestWriteAndReadStatuses.db"
|
testDB := "test/databases/TestWriteAndReadStatuses.db"
|
||||||
|
@ -122,18 +133,22 @@ func TestReadAndWriteStatuses(t *testing.T) {
|
||||||
// Begin with an initial list of statuses
|
// Begin with an initial list of statuses
|
||||||
initialStatusList := []Status{
|
initialStatusList := []Status{
|
||||||
{
|
{
|
||||||
|
ID: -1,
|
||||||
Name: "Backlog",
|
Name: "Backlog",
|
||||||
CardIds: []int{1, 14, 9, 10},
|
CardIds: []int{1, 14, 9, 10},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
ID: -1,
|
||||||
Name: "Next",
|
Name: "Next",
|
||||||
CardIds: []int{2, 5, 12},
|
CardIds: []int{2, 5, 12},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
ID: -1,
|
||||||
Name: "In progress",
|
Name: "In progress",
|
||||||
CardIds: []int{3},
|
CardIds: []int{3},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
ID: -1,
|
||||||
Name: "Finished!",
|
Name: "Finished!",
|
||||||
CardIds: []int{4, 6, 7, 8, 11, 13},
|
CardIds: []int{4, 6, 7, 8, 11, 13},
|
||||||
},
|
},
|
||||||
|
@ -146,6 +161,7 @@ func TestReadAndWriteStatuses(t *testing.T) {
|
||||||
|
|
||||||
// load the status list from the database, modify it a little and write it to the database.
|
// load the status list from the database, modify it a little and write it to the database.
|
||||||
var modifiedStatusList []Status
|
var modifiedStatusList []Status
|
||||||
|
|
||||||
if modifiedStatusList, err = loadAllStatuses(db); err != nil {
|
if modifiedStatusList, err = loadAllStatuses(db); err != nil {
|
||||||
t.Fatalf("An error occurred whilst reading the initial status list to the database, %s", err)
|
t.Fatalf("An error occurred whilst reading the initial status list to the database, %s", err)
|
||||||
}
|
}
|
||||||
|
@ -153,6 +169,7 @@ func TestReadAndWriteStatuses(t *testing.T) {
|
||||||
modifiedStatusList[2].CardIds = []int{3, 14}
|
modifiedStatusList[2].CardIds = []int{3, 14}
|
||||||
|
|
||||||
archiveStatus := Status{
|
archiveStatus := Status{
|
||||||
|
ID: -1,
|
||||||
Name: "Archived",
|
Name: "Archived",
|
||||||
CardIds: []int{34, 51, 894},
|
CardIds: []int{34, 51, 894},
|
||||||
}
|
}
|
||||||
|
@ -166,33 +183,34 @@ func TestReadAndWriteStatuses(t *testing.T) {
|
||||||
// read the final status list from the database and compare to the expected status list.
|
// read the final status list from the database and compare to the expected status list.
|
||||||
want := []Status{
|
want := []Status{
|
||||||
{
|
{
|
||||||
Id: 1,
|
ID: 1,
|
||||||
Name: "Backlog",
|
Name: "Backlog",
|
||||||
CardIds: []int{1, 14, 9, 10},
|
CardIds: []int{1, 14, 9, 10},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 2,
|
ID: 2,
|
||||||
Name: "Next",
|
Name: "Next",
|
||||||
CardIds: []int{2, 5, 12},
|
CardIds: []int{2, 5, 12},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 3,
|
ID: 3,
|
||||||
Name: "In progress",
|
Name: "In progress",
|
||||||
CardIds: []int{3, 14},
|
CardIds: []int{3, 14},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 4,
|
ID: 4,
|
||||||
Name: "Finished!",
|
Name: "Finished!",
|
||||||
CardIds: []int{4, 6, 7, 8, 11, 13},
|
CardIds: []int{4, 6, 7, 8, 11, 13},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Id: 5,
|
ID: 5,
|
||||||
Name: "Archived",
|
Name: "Archived",
|
||||||
CardIds: []int{34, 51, 894},
|
CardIds: []int{34, 51, 894},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var got []Status
|
var got []Status
|
||||||
|
|
||||||
if got, err = loadAllStatuses(db); err != nil {
|
if got, err = loadAllStatuses(db); err != nil {
|
||||||
t.Fatalf("An error occurred whilst reading the modified status list from the database, %s", err)
|
t.Fatalf("An error occurred whilst reading the modified status list from the database, %s", err)
|
||||||
}
|
}
|
||||||
|
@ -205,7 +223,10 @@ func TestReadAndWriteStatuses(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadAndWriteCards(t *testing.T) {
|
func TestReadAndWriteCards(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
var db *bolt.DB
|
var db *bolt.DB
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
testDB := "test/databases/TestReadWriteCards.db"
|
testDB := "test/databases/TestReadWriteCards.db"
|
||||||
|
@ -220,35 +241,36 @@ func TestReadAndWriteCards(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
initialCard := Card{
|
initialCard := Card{
|
||||||
|
ID: -1,
|
||||||
Title: "A test task.",
|
Title: "A test task.",
|
||||||
Content: "This task should be completed.",
|
Content: "This task should be completed.",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the card
|
// Save the card
|
||||||
cardId, err := saveCard(db, initialCard)
|
cardID, err := saveCard(db, initialCard)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("An error occurred whilst saving the initial card to the database, %s", err)
|
t.Fatalf("An error occurred whilst saving the initial card to the database, %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
modifiedCard, err := loadCard(db, cardId)
|
modifiedCard, err := loadCard(db, cardID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("An error occurred whilst loading the card from the database, %s", err)
|
t.Fatalf("An error occurred whilst loading the card from the database, %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
modifiedCard.Title = "Task: Foo bar baz."
|
modifiedCard.Title = "Task: Foo bar baz."
|
||||||
|
|
||||||
modifiedCardId, err := saveCard(db, modifiedCard)
|
modifiedCardID, err := saveCard(db, modifiedCard)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("An error occurred whilst saving the modified card to the database, %s", err)
|
t.Fatalf("An error occurred whilst saving the modified card to the database, %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
want := Card{
|
want := Card{
|
||||||
Id: 1,
|
ID: 1,
|
||||||
Title: "Task: Foo bar baz.",
|
Title: "Task: Foo bar baz.",
|
||||||
Content: "This task should be completed.",
|
Content: "This task should be completed.",
|
||||||
}
|
}
|
||||||
|
|
||||||
got, err := loadCard(db, modifiedCardId)
|
got, err := loadCard(db, modifiedCardID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("An error occurred whilst loading the modified from the database, %s", err)
|
t.Fatalf("An error occurred whilst loading the modified from the database, %s", err)
|
||||||
}
|
}
|
11
errors.go
Normal file
11
errors.go
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type bucketNotExistError struct {
|
||||||
|
bucket string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e bucketNotExistError) Error() string {
|
||||||
|
return fmt.Sprintf("bucket %s does not exist", e.bucket)
|
||||||
|
}
|
|
@ -2,14 +2,14 @@ package main
|
||||||
|
|
||||||
// Status represents the status of the Kanban board.
|
// Status represents the status of the Kanban board.
|
||||||
type Status struct {
|
type Status struct {
|
||||||
Id int
|
ID int
|
||||||
Name string
|
Name string
|
||||||
CardIds []int
|
CardIds []int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Card represents a card on a Kanban board
|
// Card represents a card on a Kanban board.
|
||||||
type Card struct {
|
type Card struct {
|
||||||
Id int
|
ID int
|
||||||
Title string
|
Title string
|
||||||
Content string
|
Content string
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,11 @@ func Test() error {
|
||||||
return goTest(args...)
|
return goTest(args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lint runs golangci-lint against the code.
|
||||||
|
func Lint() error {
|
||||||
|
return sh.RunV("golangci-lint", "run", "--color", "always")
|
||||||
|
}
|
||||||
|
|
||||||
// Build build the executable
|
// Build build the executable
|
||||||
func Build() error {
|
func Build() error {
|
||||||
return sh.Run("go", "build", "-o", binary, "main.go")
|
return sh.Run("go", "build", "-o", binary, "main.go")
|
||||||
|
|
Loading…
Reference in a new issue