Use unix timestamps for post.Store

This commit is contained in:
Brian Picciano 2022-05-06 21:49:00 -06:00
parent f7d72adfb5
commit c99b37c5b3
2 changed files with 47 additions and 32 deletions

View File

@ -6,10 +6,10 @@ import (
"fmt"
"path"
"strings"
"time"
_ "github.com/mattn/go-sqlite3"
_ "github.com/mattn/go-sqlite3" // we need dis
migrate "github.com/rubenv/sql-migrate"
"github.com/tilinna/clock"
)
var (
@ -23,8 +23,8 @@ var (
type StoredPost struct {
Post
PublishedAt Date
LastUpdatedAt Date
PublishedAt time.Time
LastUpdatedAt time.Time
}
// URL returns the relative URL of the StoredPost.
@ -32,9 +32,9 @@ func (p StoredPost) URL() string {
return path.Join(
fmt.Sprintf(
"%d/%0d/%0d",
p.PublishedAt.Year,
p.PublishedAt.Month,
p.PublishedAt.Day,
p.PublishedAt.Year(),
p.PublishedAt.Month(),
p.PublishedAt.Day(),
),
p.ID+".html",
)
@ -45,7 +45,7 @@ type Store interface {
// Set sets the Post data into the storage, keyed by the Post's ID. It
// overwrites a previous Post with the same ID, if there was one.
Set(Post) error
Set(post Post, now time.Time) error
// Get returns count StoredPosts, sorted time descending, offset by the given page
// number. The returned boolean indicates if there are more pages or not.
@ -102,8 +102,6 @@ type StoreParams struct {
// Path to the file the database will be stored at.
DBFilePath string
Clock clock.Clock
}
type store struct {
@ -136,7 +134,7 @@ func (s *store) Close() error {
return s.db.Close()
}
// if the callback returns an error then the transaction is aborted
// if the callback returns an error then the transaction is aborted.
func (s *store) withTx(cb func(*sql.Tx) error) error {
tx, err := s.db.Begin()
@ -164,9 +162,12 @@ func (s *store) withTx(cb func(*sql.Tx) error) error {
return nil
}
func (s *store) Set(post Post) error {
func (s *store) Set(post Post, now time.Time) error {
return s.withTx(func(tx *sql.Tx) error {
currentDate := DateFromTime(s.params.Clock.Now())
nowTS := now.Unix()
nowSql := sql.NullInt64{Int64: nowTS, Valid: !now.IsZero()}
_, err := tx.Exec(
`INSERT INTO posts (
@ -184,9 +185,9 @@ func (s *store) Set(post Post) error {
post.Title,
post.Description,
&sql.NullString{String: post.Series, Valid: post.Series != ""},
currentDate,
nowSql,
post.Body,
currentDate,
nowSql,
)
if err != nil {
@ -259,11 +260,12 @@ func (s *store) get(
var (
post StoredPost
series, tag sql.NullString
publishedAt, lastUpdatedAt sql.NullInt64
)
err := rows.Scan(
&post.ID, &post.Title, &post.Description, &series, &tag,
&post.PublishedAt, &post.LastUpdatedAt,
&publishedAt, &lastUpdatedAt,
&post.Body,
)
@ -283,6 +285,14 @@ func (s *store) get(
post.Series = series.String
if publishedAt.Valid {
post.PublishedAt = time.Unix(publishedAt.Int64, 0).UTC()
}
if lastUpdatedAt.Valid {
post.LastUpdatedAt = time.Unix(lastUpdatedAt.Int64, 0).UTC()
}
posts = append(posts, post)
}

View File

@ -29,7 +29,7 @@ type storeTestHarness struct {
func newStoreTestHarness(t *testing.T) storeTestHarness {
clock := clock.NewMock(time.Now().Truncate(1 * time.Hour))
clock := clock.NewMock(time.Now().UTC().Truncate(1 * time.Hour))
tmpFile, err := ioutil.TempFile(os.TempDir(), "mediocre-blog-post-store-test-")
if err != nil {
@ -48,7 +48,6 @@ func newStoreTestHarness(t *testing.T) storeTestHarness {
store, err := NewStore(StoreParams{
DBFilePath: tmpFilePath,
Clock: clock,
})
assert.NoError(t, err)
@ -66,7 +65,7 @@ func (h *storeTestHarness) testStoredPost(i int) StoredPost {
post := testPost(i)
return StoredPost{
Post: post,
PublishedAt: DateFromTime(h.clock.Now()),
PublishedAt: h.clock.Now(),
}
}
@ -101,41 +100,41 @@ func TestStore(t *testing.T) {
t.Run("set_get_delete", func(t *testing.T) {
h := newStoreTestHarness(t)
nowDate := DateFromTime(h.clock.Now())
now := h.clock.Now().UTC()
post := testPost(0)
post.Tags = []string{"foo", "bar"}
assert.NoError(t, h.store.Set(post))
assert.NoError(t, h.store.Set(post, now))
gotPost, err := h.store.GetByID(post.ID)
assert.NoError(t, err)
assertPostEqual(t, StoredPost{
Post: post,
PublishedAt: nowDate,
PublishedAt: now,
}, gotPost)
// we will now try updating the post on a different day, and ensure it
// updates properly
h.clock.Add(24 * time.Hour)
newNowDate := DateFromTime(h.clock.Now())
newNow := h.clock.Now().UTC()
post.Title = "something else"
post.Series = "whatever"
post.Body = "anything"
post.Tags = []string{"bar", "baz"}
assert.NoError(t, h.store.Set(post))
assert.NoError(t, h.store.Set(post, newNow))
gotPost, err = h.store.GetByID(post.ID)
assert.NoError(t, err)
assertPostEqual(t, StoredPost{
Post: post,
PublishedAt: nowDate,
LastUpdatedAt: newNowDate,
PublishedAt: now,
LastUpdatedAt: newNow,
}, gotPost)
// delete the post, it should go away
@ -148,6 +147,8 @@ func TestStore(t *testing.T) {
t.Run("get", func(t *testing.T) {
h := newStoreTestHarness(t)
now := h.clock.Now().UTC()
posts := []StoredPost{
h.testStoredPost(0),
h.testStoredPost(1),
@ -156,7 +157,7 @@ func TestStore(t *testing.T) {
}
for _, post := range posts {
assert.NoError(t, h.store.Set(post.Post))
assert.NoError(t, h.store.Set(post.Post, now))
}
gotPosts, hasMore, err := h.store.Get(0, 2)
@ -170,7 +171,7 @@ func TestStore(t *testing.T) {
assertPostsEqual(t, posts[2:4], gotPosts)
posts = append(posts, h.testStoredPost(4))
assert.NoError(t, h.store.Set(posts[4].Post))
assert.NoError(t, h.store.Set(posts[4].Post, now))
gotPosts, hasMore, err = h.store.Get(1, 2)
assert.NoError(t, err)
@ -186,6 +187,8 @@ func TestStore(t *testing.T) {
t.Run("get_by_series", func(t *testing.T) {
h := newStoreTestHarness(t)
now := h.clock.Now().UTC()
posts := []StoredPost{
h.testStoredPost(0),
h.testStoredPost(1),
@ -198,7 +201,7 @@ func TestStore(t *testing.T) {
posts[2].Series = "bar"
for _, post := range posts {
assert.NoError(t, h.store.Set(post.Post))
assert.NoError(t, h.store.Set(post.Post, now))
}
fooPosts, err := h.store.GetBySeries("foo")
@ -218,6 +221,8 @@ func TestStore(t *testing.T) {
h := newStoreTestHarness(t)
now := h.clock.Now().UTC()
posts := []StoredPost{
h.testStoredPost(0),
h.testStoredPost(1),
@ -230,7 +235,7 @@ func TestStore(t *testing.T) {
posts[2].Tags = []string{"bar"}
for _, post := range posts {
assert.NoError(t, h.store.Set(post.Post))
assert.NoError(t, h.store.Set(post.Post, now))
}
fooPosts, err := h.store.GetByTag("foo")