Skip to content

Commit

Permalink
🚧 Add snapshot package
Browse files Browse the repository at this point in the history
Snapshots allow for storing and retreiving of commits. This is a
preliminary stage to allow for saving incomplete or failed commits.

The author field uses the repository user. Adding tags was necessary
to allow empty authors to be omitted from the snapshot file.
  • Loading branch information
mikelorant committed Feb 1, 2023
1 parent d392cd9 commit 00f37de
Show file tree
Hide file tree
Showing 3 changed files with 268 additions and 3 deletions.
6 changes: 3 additions & 3 deletions internal/repository/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (
)

type User struct {
Name string
Email string
Default bool
Name string `yaml:"name,omitempty"`
Email string `yaml:"email,omitempty"`
Default bool `yaml:"default,omitempty"`
}

func (r *Repository) Users() ([]User, error) {
Expand Down
56 changes: 56 additions & 0 deletions internal/snapshot/snapshot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package snapshot

import (
"errors"
"fmt"
"io"

"github.com/mikelorant/committed/internal/repository"
"gopkg.in/yaml.v3"
)

type Snapshot struct {
Emoji string `yaml:"emoji,omitempty"`
Summary string `yaml:"summary,omitempty"`
Body string `yaml:"body,omitempty"`
Footer string `yaml:"footer,omitempty"`
Author repository.User `yaml:"author,omitempty"`
Amend bool `yaml:"amend,omitempty"`
}

var (
errReader = errors.New("empty reader")
errWriter = errors.New("empty writer")
)

func (s *Snapshot) Load(fh io.Reader) (Snapshot, error) {
var snap Snapshot

if fh == nil {
return snap, errReader
}

err := yaml.NewDecoder(fh).Decode(&snap)
switch {
case err == nil:
case errors.Is(err, io.EOF):
default:
return snap, fmt.Errorf("unable to decode snapshot: %w", err)
}

return snap, nil
}

func (s *Snapshot) Save(fh io.WriteCloser, snap Snapshot) error {
if fh == nil {
return errWriter
}

err := yaml.NewEncoder(fh).Encode(&snap)
if err != nil {
return fmt.Errorf("unable to encode snapshot: %w", err)
}
defer fh.Close()

return nil
}
209 changes: 209 additions & 0 deletions internal/snapshot/snapshot_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
package snapshot_test

import (
"bytes"
"errors"
"io"
"strings"
"testing"

"github.com/MakeNowJust/heredoc/v2"
"github.com/mikelorant/committed/internal/repository"
"github.com/mikelorant/committed/internal/snapshot"
"github.com/stretchr/testify/assert"
)

type readWriteCloser struct {
bytes.Buffer
}

func (wc *readWriteCloser) Close() error {
return nil
}

type errorReadWriteCloser struct {
readWriteCloser
}

func (t *errorReadWriteCloser) Write(p []byte) (n int, err error) {
return 0, errMock
}

var errMock = errors.New("error")

func TestLoad(t *testing.T) {
type args struct {
reader io.Reader
}
type want struct {
snapshot snapshot.Snapshot
err string
}

tests := []struct {
name string
args args
want want
}{
{
name: "data",
args: args{
reader: strings.NewReader(heredoc.Doc(`
emoji: ":art:"
summary: summary
body: body
footer: footer
author:
name: John Doe
email: [email protected]
amend: true
`)),
},
want: want{
snapshot: snapshot.Snapshot{
Emoji: ":art:",
Summary: "summary",
Body: "body",
Footer: "footer",
Author: repository.User{
Name: "John Doe",
Email: "[email protected]",
},
Amend: true,
},
},
},
{
name: "empty",
args: args{
reader: strings.NewReader(""),
},
},
{
name: "error_reader",
want: want{
err: "empty reader",
},
},
{
name: "error_eof",
args: args{
reader: io.LimitReader(strings.NewReader("summary: summary"), 0),
},
},
{
name: "error_decode",
args: args{
reader: io.LimitReader(strings.NewReader("summary: summary"), 1),
},
want: want{
err: "unable to decode snapshot",
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var s snapshot.Snapshot

snap, err := s.Load(tt.args.reader)

if tt.want.err != "" {
assert.Error(t, err)
assert.ErrorContains(t, err, tt.want.err)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.want.snapshot, snap)
})
}
}

func TestSave(t *testing.T) {
type args struct {
writer io.ReadWriteCloser
snapshot snapshot.Snapshot
}

type want struct {
data string
err string
}

tests := []struct {
name string
args args
want want
}{
{
name: "data",
args: args{
writer: new(readWriteCloser),
snapshot: snapshot.Snapshot{
Emoji: ":art:",
Summary: "summary",
Body: "body",
Footer: "footer",
Author: repository.User{
Name: "John Doe",
Email: "[email protected]",
},
Amend: true,
},
},
want: want{
data: heredoc.Doc(`
emoji: ':art:'
summary: summary
body: body
footer: footer
author:
name: John Doe
email: [email protected]
amend: true
`),
},
},
{
name: "empty",
args: args{
writer: new(readWriteCloser),
},
want: want{
data: "{}\n",
},
},
{
name: "error_encode",
args: args{
writer: new(errorReadWriteCloser),
},
want: want{
err: "unable to encode snapshot",
},
},
{
name: "error_writer",
want: want{
err: "empty writer",
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var s snapshot.Snapshot

err := s.Save(tt.args.writer, tt.args.snapshot)
if tt.want.err != "" {
assert.Error(t, err)
assert.ErrorContains(t, err, tt.want.err)
return
}
assert.NoError(t, err)

got, _ := io.ReadAll(tt.args.writer)
assert.Equal(t, tt.want.data, string(got))
})
}
}

0 comments on commit 00f37de

Please sign in to comment.