Add Appearance type with tests
parent
63ff9c3fa9
commit
31b6e1202c
@ -0,0 +1,87 @@
|
||||
package dbi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Appearance struct {
|
||||
UID string `json:"uid"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Hyperlink string `json:"hyperlink"`
|
||||
Status string `json:"status"`
|
||||
Created string `json:"created"`
|
||||
Modified string `json:"modified"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
func CreateAppearance(conn *sql.Conn, a *Appearance) error {
|
||||
if a.UID == "" {
|
||||
a.UID = GenUUID()
|
||||
}
|
||||
now := GetNow()
|
||||
a.Created = now
|
||||
a.Modified = now
|
||||
_, err := conn.ExecContext(context.Background(),
|
||||
`INSERT INTO appearances (UID, Title, Description, Hyperlink, Status, Created, Modified, Username)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
a.UID, a.Title, a.Description, a.Hyperlink, a.Status, a.Created, a.Modified, a.Username,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func GetAppearance(conn *sql.Conn, uid string) (Appearance, error) {
|
||||
row := conn.QueryRowContext(context.Background(),
|
||||
`SELECT UID, Title, Description, Hyperlink, Status, Created, Modified, Username
|
||||
FROM appearances WHERE UID = ?`,
|
||||
uid,
|
||||
)
|
||||
var a Appearance
|
||||
err := row.Scan(&a.UID, &a.Title, &a.Description, &a.Hyperlink, &a.Status, &a.Created, &a.Modified, &a.Username)
|
||||
if err == sql.ErrNoRows {
|
||||
return Appearance{}, fmt.Errorf("appearance %q not found", uid)
|
||||
}
|
||||
return a, err
|
||||
}
|
||||
|
||||
func UpdateAppearance(conn *sql.Conn, a *Appearance) error {
|
||||
a.Modified = GetNow()
|
||||
_, err := conn.ExecContext(context.Background(),
|
||||
`UPDATE appearances SET Title = ?, Description = ?, Hyperlink = ?, Status = ?, Modified = ?, Username = ?
|
||||
WHERE UID = ?`,
|
||||
a.Title, a.Description, a.Hyperlink, a.Status, a.Modified, a.Username, a.UID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func DeleteAppearance(conn *sql.Conn, uid string) error {
|
||||
_, err := conn.ExecContext(context.Background(),
|
||||
`DELETE FROM appearances WHERE UID = ?`,
|
||||
uid,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func GetAppearancesForUsername(conn *sql.Conn, username string) ([]Appearance, error) {
|
||||
rows, err := conn.QueryContext(context.Background(),
|
||||
`SELECT UID, Title, Description, Hyperlink, Status, Created, Modified, Username
|
||||
FROM appearances WHERE Username = ?`,
|
||||
username,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var results []Appearance
|
||||
for rows.Next() {
|
||||
var a Appearance
|
||||
if err := rows.Scan(&a.UID, &a.Title, &a.Description, &a.Hyperlink, &a.Status, &a.Created, &a.Modified, &a.Username); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, a)
|
||||
}
|
||||
return results, rows.Err()
|
||||
}
|
||||
@ -0,0 +1,206 @@
|
||||
package dbi
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func genAppearance(username string) Appearance {
|
||||
return Appearance{
|
||||
Title: randString(20),
|
||||
Description: randString(60),
|
||||
Hyperlink: "https://" + randString(12) + ".example.com",
|
||||
Status: "reported",
|
||||
Username: username,
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateAppearance(t *testing.T) {
|
||||
conn := testConn(t, testDB(t))
|
||||
a := genAppearance(randString(10))
|
||||
|
||||
if err := CreateAppearance(conn, &a); err != nil {
|
||||
t.Fatalf("CreateAppearance: %v", err)
|
||||
}
|
||||
if a.UID == "" {
|
||||
t.Error("expected UID to be set after create")
|
||||
}
|
||||
if a.Created == "" {
|
||||
t.Error("expected Created to be set after create")
|
||||
}
|
||||
if a.Modified == "" {
|
||||
t.Error("expected Modified to be set after create")
|
||||
}
|
||||
|
||||
got, err := GetAppearance(conn, a.UID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetAppearance after create: %v", err)
|
||||
}
|
||||
if got != a {
|
||||
t.Errorf("got %+v, want %+v", got, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateAppearancePresetUID(t *testing.T) {
|
||||
conn := testConn(t, testDB(t))
|
||||
a := genAppearance(randString(10))
|
||||
a.UID = "preset" + randString(10)
|
||||
presetUID := a.UID
|
||||
|
||||
if err := CreateAppearance(conn, &a); err != nil {
|
||||
t.Fatalf("CreateAppearance: %v", err)
|
||||
}
|
||||
if a.UID != presetUID {
|
||||
t.Error("CreateAppearance should not overwrite a non-empty UID")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateAppearanceDuplicateUID(t *testing.T) {
|
||||
conn := testConn(t, testDB(t))
|
||||
a := genAppearance(randString(10))
|
||||
|
||||
if err := CreateAppearance(conn, &a); err != nil {
|
||||
t.Fatalf("first CreateAppearance: %v", err)
|
||||
}
|
||||
a2 := a
|
||||
if err := CreateAppearance(conn, &a2); err == nil {
|
||||
t.Error("expected error on duplicate UID, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAppearance(t *testing.T) {
|
||||
conn := testConn(t, testDB(t))
|
||||
a := genAppearance(randString(10))
|
||||
|
||||
if err := CreateAppearance(conn, &a); err != nil {
|
||||
t.Fatalf("CreateAppearance: %v", err)
|
||||
}
|
||||
|
||||
got, err := GetAppearance(conn, a.UID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetAppearance: %v", err)
|
||||
}
|
||||
if got != a {
|
||||
t.Errorf("got %+v, want %+v", got, a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAppearanceNotFound(t *testing.T) {
|
||||
conn := testConn(t, testDB(t))
|
||||
|
||||
_, err := GetAppearance(conn, randString(32))
|
||||
if err == nil {
|
||||
t.Error("expected error for missing appearance, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateAppearance(t *testing.T) {
|
||||
conn := testConn(t, testDB(t))
|
||||
a := genAppearance(randString(10))
|
||||
|
||||
if err := CreateAppearance(conn, &a); err != nil {
|
||||
t.Fatalf("CreateAppearance: %v", err)
|
||||
}
|
||||
|
||||
originalUID := a.UID
|
||||
originalCreated := a.Created
|
||||
a.Title = randString(20)
|
||||
a.Description = randString(60)
|
||||
a.Hyperlink = "https://" + randString(12) + ".example.com"
|
||||
a.Status = "posted"
|
||||
|
||||
if err := UpdateAppearance(conn, &a); err != nil {
|
||||
t.Fatalf("UpdateAppearance: %v", err)
|
||||
}
|
||||
|
||||
got, err := GetAppearance(conn, a.UID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetAppearance after update: %v", err)
|
||||
}
|
||||
if got.UID != originalUID {
|
||||
t.Errorf("UID changed: got %q, want %q", got.UID, originalUID)
|
||||
}
|
||||
if got.Created != originalCreated {
|
||||
t.Errorf("Created changed: got %q, want %q", got.Created, originalCreated)
|
||||
}
|
||||
if got.Title != a.Title {
|
||||
t.Errorf("Title not updated: got %q, want %q", got.Title, a.Title)
|
||||
}
|
||||
if got.Description != a.Description {
|
||||
t.Errorf("Description not updated: got %q, want %q", got.Description, a.Description)
|
||||
}
|
||||
if got.Status != "posted" {
|
||||
t.Errorf("Status not updated: got %q", got.Status)
|
||||
}
|
||||
if got.Modified == "" {
|
||||
t.Error("Modified should be set after update")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteAppearance(t *testing.T) {
|
||||
conn := testConn(t, testDB(t))
|
||||
a := genAppearance(randString(10))
|
||||
|
||||
if err := CreateAppearance(conn, &a); err != nil {
|
||||
t.Fatalf("CreateAppearance: %v", err)
|
||||
}
|
||||
|
||||
if err := DeleteAppearance(conn, a.UID); err != nil {
|
||||
t.Fatalf("DeleteAppearance: %v", err)
|
||||
}
|
||||
|
||||
_, err := GetAppearance(conn, a.UID)
|
||||
if err == nil {
|
||||
t.Error("expected error after delete, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAppearancesForUsername(t *testing.T) {
|
||||
conn := testConn(t, testDB(t))
|
||||
username := randString(10)
|
||||
|
||||
items := make([]Appearance, 3)
|
||||
for i := range items {
|
||||
items[i] = genAppearance(username)
|
||||
if err := CreateAppearance(conn, &items[i]); err != nil {
|
||||
t.Fatalf("CreateAppearance %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
other := genAppearance(randString(10))
|
||||
if err := CreateAppearance(conn, &other); err != nil {
|
||||
t.Fatalf("CreateAppearance other: %v", err)
|
||||
}
|
||||
|
||||
got, err := GetAppearancesForUsername(conn, username)
|
||||
if err != nil {
|
||||
t.Fatalf("GetAppearancesForUsername: %v", err)
|
||||
}
|
||||
if len(got) != 3 {
|
||||
t.Errorf("got %d appearances, want 3", len(got))
|
||||
}
|
||||
byUID := make(map[string]Appearance)
|
||||
for _, a := range got {
|
||||
byUID[a.UID] = a
|
||||
}
|
||||
for _, want := range items {
|
||||
a, ok := byUID[want.UID]
|
||||
if !ok {
|
||||
t.Errorf("appearance %q missing from results", want.UID)
|
||||
continue
|
||||
}
|
||||
if a != want {
|
||||
t.Errorf("appearance %q: got %+v, want %+v", want.UID, a, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAppearancesForUsernameEmpty(t *testing.T) {
|
||||
conn := testConn(t, testDB(t))
|
||||
|
||||
got, err := GetAppearancesForUsername(conn, randString(10))
|
||||
if err != nil {
|
||||
t.Fatalf("GetAppearancesForUsername: %v", err)
|
||||
}
|
||||
if len(got) != 0 {
|
||||
t.Errorf("expected empty slice, got %d items", len(got))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue