aboutsummaryrefslogtreecommitdiffstats
path: root/bug
diff options
context:
space:
mode:
Diffstat (limited to 'bug')
-rw-r--r--bug/bug_actions_test.go149
-rw-r--r--bug/bug_test.go6
-rw-r--r--bug/op_add_comment.go54
-rw-r--r--bug/op_add_comment_test.go25
-rw-r--r--bug/op_create.go59
-rw-r--r--bug/op_create_test.go17
-rw-r--r--bug/op_edit_comment.go58
-rw-r--r--bug/op_edit_comment_test.go18
-rw-r--r--bug/op_label_change.go53
-rw-r--r--bug/op_label_change_test.go25
-rw-r--r--bug/op_noop.go42
-rw-r--r--bug/op_noop_test.go25
-rw-r--r--bug/op_set_metadata.go54
-rw-r--r--bug/op_set_metadata_test.go19
-rw-r--r--bug/op_set_status.go49
-rw-r--r--bug/op_set_status_test.go25
-rw-r--r--bug/op_set_title.go53
-rw-r--r--bug/op_set_title_test.go25
-rw-r--r--bug/operation.go36
-rw-r--r--bug/operation_iterator_test.go8
-rw-r--r--bug/operation_pack.go8
21 files changed, 695 insertions, 113 deletions
diff --git a/bug/bug_actions_test.go b/bug/bug_actions_test.go
index 4327ae58..95ca01c9 100644
--- a/bug/bug_actions_test.go
+++ b/bug/bug_actions_test.go
@@ -77,17 +77,20 @@ func TestPushPull(t *testing.T) {
repoA, repoB, remote := setupRepos(t)
defer cleanupRepos(repoA, repoB, remote)
+ err := rene.Commit(repoA)
+ assert.NoError(t, err)
+
bug1, _, err := Create(rene, unix, "bug1", "message")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug1.Commit(repoA)
- assert.Nil(t, err)
+ assert.NoError(t, err)
// A --> remote --> B
_, err = Push(repoA, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = Pull(repoB, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
bugs := allBugs(t, ReadAllLocalBugs(repoB))
@@ -97,15 +100,15 @@ func TestPushPull(t *testing.T) {
// B --> remote --> A
bug2, _, err := Create(rene, unix, "bug2", "message")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug2.Commit(repoB)
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = Push(repoB, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = Pull(repoA, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
bugs = allBugs(t, ReadAllLocalBugs(repoA))
@@ -140,37 +143,37 @@ func _RebaseTheirs(t testing.TB) {
defer cleanupRepos(repoA, repoB, remote)
bug1, _, err := Create(rene, unix, "bug1", "message")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug1.Commit(repoA)
- assert.Nil(t, err)
+ assert.NoError(t, err)
// A --> remote
_, err = Push(repoA, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
// remote --> B
err = Pull(repoB, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
bug2, err := ReadLocalBug(repoB, bug1.Id())
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug2, rene, unix, "message2")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug2, rene, unix, "message3")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug2, rene, unix, "message4")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug2.Commit(repoB)
- assert.Nil(t, err)
+ assert.NoError(t, err)
// B --> remote
_, err = Push(repoB, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
// remote --> A
err = Pull(repoA, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
bugs := allBugs(t, ReadAllLocalBugs(repoB))
@@ -179,7 +182,7 @@ func _RebaseTheirs(t testing.TB) {
}
bug3, err := ReadLocalBug(repoA, bug1.Id())
- assert.Nil(t, err)
+ assert.NoError(t, err)
if nbOps(bug3) != 4 {
t.Fatal("Unexpected number of operations")
@@ -201,48 +204,48 @@ func _RebaseOurs(t testing.TB) {
defer cleanupRepos(repoA, repoB, remote)
bug1, _, err := Create(rene, unix, "bug1", "message")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug1.Commit(repoA)
- assert.Nil(t, err)
+ assert.NoError(t, err)
// A --> remote
_, err = Push(repoA, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
// remote --> B
err = Pull(repoB, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message2")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message3")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message4")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug1.Commit(repoA)
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message5")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message6")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message7")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug1.Commit(repoA)
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message8")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message9")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message10")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug1.Commit(repoA)
- assert.Nil(t, err)
+ assert.NoError(t, err)
// remote --> A
err = Pull(repoA, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
bugs := allBugs(t, ReadAllLocalBugs(repoA))
@@ -251,7 +254,7 @@ func _RebaseOurs(t testing.TB) {
}
bug2, err := ReadLocalBug(repoA, bug1.Id())
- assert.Nil(t, err)
+ assert.NoError(t, err)
if nbOps(bug2) != 10 {
t.Fatal("Unexpected number of operations")
@@ -282,82 +285,82 @@ func _RebaseConflict(t testing.TB) {
defer cleanupRepos(repoA, repoB, remote)
bug1, _, err := Create(rene, unix, "bug1", "message")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug1.Commit(repoA)
- assert.Nil(t, err)
+ assert.NoError(t, err)
// A --> remote
_, err = Push(repoA, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
// remote --> B
err = Pull(repoB, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message2")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message3")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message4")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug1.Commit(repoA)
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message5")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message6")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message7")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug1.Commit(repoA)
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message8")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message9")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug1, rene, unix, "message10")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug1.Commit(repoA)
- assert.Nil(t, err)
+ assert.NoError(t, err)
bug2, err := ReadLocalBug(repoB, bug1.Id())
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug2, rene, unix, "message11")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug2, rene, unix, "message12")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug2, rene, unix, "message13")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug2.Commit(repoB)
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug2, rene, unix, "message14")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug2, rene, unix, "message15")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug2, rene, unix, "message16")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug2.Commit(repoB)
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug2, rene, unix, "message17")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug2, rene, unix, "message18")
- assert.Nil(t, err)
+ assert.NoError(t, err)
_, err = AddComment(bug2, rene, unix, "message19")
- assert.Nil(t, err)
+ assert.NoError(t, err)
err = bug2.Commit(repoB)
- assert.Nil(t, err)
+ assert.NoError(t, err)
// A --> remote
_, err = Push(repoA, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
// remote --> B
err = Pull(repoB, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
bugs := allBugs(t, ReadAllLocalBugs(repoB))
@@ -366,7 +369,7 @@ func _RebaseConflict(t testing.TB) {
}
bug3, err := ReadLocalBug(repoB, bug1.Id())
- assert.Nil(t, err)
+ assert.NoError(t, err)
if nbOps(bug3) != 19 {
t.Fatal("Unexpected number of operations")
@@ -374,11 +377,11 @@ func _RebaseConflict(t testing.TB) {
// B --> remote
_, err = Push(repoB, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
// remote --> A
err = Pull(repoA, "origin")
- assert.Nil(t, err)
+ assert.NoError(t, err)
bugs = allBugs(t, ReadAllLocalBugs(repoA))
@@ -387,7 +390,7 @@ func _RebaseConflict(t testing.TB) {
}
bug4, err := ReadLocalBug(repoA, bug1.Id())
- assert.Nil(t, err)
+ assert.NoError(t, err)
if nbOps(bug4) != 19 {
t.Fatal("Unexpected number of operations")
diff --git a/bug/bug_test.go b/bug/bug_test.go
index 0fd373d5..41a5b03d 100644
--- a/bug/bug_test.go
+++ b/bug/bug_test.go
@@ -2,7 +2,6 @@ package bug
import (
"github.com/MichaelMure/git-bug/repository"
- "github.com/go-test/deep"
"github.com/stretchr/testify/assert"
"testing"
@@ -87,8 +86,5 @@ func TestBugSerialisation(t *testing.T) {
}
}
- deep.CompareUnexportedFields = true
- if diff := deep.Equal(bug1, bug2); diff != nil {
- t.Fatal(diff)
- }
+ assert.Equal(t, bug1, bug2)
}
diff --git a/bug/op_add_comment.go b/bug/op_add_comment.go
index 23a10419..ba5d611e 100644
--- a/bug/op_add_comment.go
+++ b/bug/op_add_comment.go
@@ -1,10 +1,10 @@
package bug
import (
+ "encoding/json"
"fmt"
"github.com/MichaelMure/git-bug/identity"
-
"github.com/MichaelMure/git-bug/util/git"
"github.com/MichaelMure/git-bug/util/text"
)
@@ -14,9 +14,9 @@ var _ Operation = &AddCommentOperation{}
// AddCommentOperation will add a new comment in the bug
type AddCommentOperation struct {
OpBase
- Message string `json:"message"`
+ Message string
// TODO: change for a map[string]util.hash to store the filename ?
- Files []git.Hash `json:"files"`
+ Files []git.Hash
}
func (op *AddCommentOperation) base() *OpBase {
@@ -67,6 +67,54 @@ func (op *AddCommentOperation) Validate() error {
return nil
}
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *AddCommentOperation) MarshalJSON() ([]byte, error) {
+ base, err := json.Marshal(op.OpBase)
+ if err != nil {
+ return nil, err
+ }
+
+ // revert back to a flat map to be able to add our own fields
+ var data map[string]interface{}
+ if err := json.Unmarshal(base, &data); err != nil {
+ return nil, err
+ }
+
+ data["message"] = op.Message
+ data["files"] = op.Files
+
+ return json.Marshal(data)
+}
+
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *AddCommentOperation) UnmarshalJSON(data []byte) error {
+ // Unmarshal OpBase and the op separately
+
+ base := OpBase{}
+ err := json.Unmarshal(data, &base)
+ if err != nil {
+ return err
+ }
+
+ aux := struct {
+ Message string `json:"message"`
+ Files []git.Hash `json:"files"`
+ }{}
+
+ err = json.Unmarshal(data, &aux)
+ if err != nil {
+ return err
+ }
+
+ op.OpBase = base
+ op.Message = aux.Message
+ op.Files = aux.Files
+
+ return nil
+}
+
// Sign post method for gqlgen
func (op *AddCommentOperation) IsAuthored() {}
diff --git a/bug/op_add_comment_test.go b/bug/op_add_comment_test.go
new file mode 100644
index 00000000..a38d0228
--- /dev/null
+++ b/bug/op_add_comment_test.go
@@ -0,0 +1,25 @@
+package bug
+
+import (
+ "encoding/json"
+ "testing"
+ "time"
+
+ "github.com/MichaelMure/git-bug/identity"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestAddCommentSerialize(t *testing.T) {
+ var rene = identity.NewBare("René Descartes", "rene@descartes.fr")
+ unix := time.Now().Unix()
+ before := NewAddCommentOp(rene, unix, "message", nil)
+
+ data, err := json.Marshal(before)
+ assert.NoError(t, err)
+
+ var after AddCommentOperation
+ err = json.Unmarshal(data, &after)
+ assert.NoError(t, err)
+
+ assert.Equal(t, before, &after)
+}
diff --git a/bug/op_create.go b/bug/op_create.go
index 01b2bf03..1d157e67 100644
--- a/bug/op_create.go
+++ b/bug/op_create.go
@@ -1,11 +1,11 @@
package bug
import (
+ "encoding/json"
"fmt"
"strings"
"github.com/MichaelMure/git-bug/identity"
-
"github.com/MichaelMure/git-bug/util/git"
"github.com/MichaelMure/git-bug/util/text"
)
@@ -15,9 +15,9 @@ var _ Operation = &CreateOperation{}
// CreateOperation define the initial creation of a bug
type CreateOperation struct {
OpBase
- Title string `json:"title"`
- Message string `json:"message"`
- Files []git.Hash `json:"files"`
+ Title string
+ Message string
+ Files []git.Hash
}
func (op *CreateOperation) base() *OpBase {
@@ -83,6 +83,57 @@ func (op *CreateOperation) Validate() error {
return nil
}
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *CreateOperation) MarshalJSON() ([]byte, error) {
+ base, err := json.Marshal(op.OpBase)
+ if err != nil {
+ return nil, err
+ }
+
+ // revert back to a flat map to be able to add our own fields
+ var data map[string]interface{}
+ if err := json.Unmarshal(base, &data); err != nil {
+ return nil, err
+ }
+
+ data["title"] = op.Title
+ data["message"] = op.Message
+ data["files"] = op.Files
+
+ return json.Marshal(data)
+}
+
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *CreateOperation) UnmarshalJSON(data []byte) error {
+ // Unmarshal OpBase and the op separately
+
+ base := OpBase{}
+ err := json.Unmarshal(data, &base)
+ if err != nil {
+ return err
+ }
+
+ aux := struct {
+ Title string `json:"title"`
+ Message string `json:"message"`
+ Files []git.Hash `json:"files"`
+ }{}
+
+ err = json.Unmarshal(data, &aux)
+ if err != nil {
+ return err
+ }
+
+ op.OpBase = base
+ op.Title = aux.Title
+ op.Message = aux.Message
+ op.Files = aux.Files
+
+ return nil
+}
+
// Sign post method for gqlgen
func (op *CreateOperation) IsAuthored() {}
diff --git a/bug/op_create_test.go b/bug/op_create_test.go
index aff58acc..31693a4a 100644
--- a/bug/op_create_test.go
+++ b/bug/op_create_test.go
@@ -1,11 +1,13 @@
package bug
import (
+ "encoding/json"
"testing"
"time"
"github.com/MichaelMure/git-bug/identity"
"github.com/go-test/deep"
+ "github.com/stretchr/testify/assert"
)
func TestCreate(t *testing.T) {
@@ -45,3 +47,18 @@ func TestCreate(t *testing.T) {
t.Fatal(diff)
}
}
+
+func TestCreateSerialize(t *testing.T) {
+ var rene = identity.NewBare("René Descartes", "rene@descartes.fr")
+ unix := time.Now().Unix()
+ before := NewCreateOp(rene, unix, "title", "message", nil)
+
+ data, err := json.Marshal(before)
+ assert.NoError(t, err)
+
+ var after CreateOperation
+ err = json.Unmarshal(data, &after)
+ assert.NoError(t, err)
+
+ assert.Equal(t, before, &after)
+}
diff --git a/bug/op_edit_comment.go b/bug/op_edit_comment.go
index 9e0afc02..3ff16653 100644
--- a/bug/op_edit_comment.go
+++ b/bug/op_edit_comment.go
@@ -1,6 +1,7 @@
package bug
import (
+ "encoding/json"
"fmt"
"github.com/MichaelMure/git-bug/identity"
@@ -14,9 +15,9 @@ var _ Operation = &EditCommentOperation{}
// EditCommentOperation will change a comment in the bug
type EditCommentOperation struct {
OpBase
- Target git.Hash `json:"target"`
- Message string `json:"message"`
- Files []git.Hash `json:"files"`
+ Target git.Hash
+ Message string
+ Files []git.Hash
}
func (op *EditCommentOperation) base() *OpBase {
@@ -94,6 +95,57 @@ func (op *EditCommentOperation) Validate() error {
return nil
}
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *EditCommentOperation) MarshalJSON() ([]byte, error) {
+ base, err := json.Marshal(op.OpBase)
+ if err != nil {
+ return nil, err
+ }
+
+ // revert back to a flat map to be able to add our own fields
+ var data map[string]interface{}
+ if err := json.Unmarshal(base, &data); err != nil {
+ return nil, err
+ }
+
+ data["target"] = op.Target
+ data["message"] = op.Message
+ data["files"] = op.Files
+
+ return json.Marshal(data)
+}
+
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *EditCommentOperation) UnmarshalJSON(data []byte) error {
+ // Unmarshal OpBase and the op separately
+
+ base := OpBase{}
+ err := json.Unmarshal(data, &base)
+ if err != nil {
+ return err
+ }
+
+ aux := struct {
+ Target git.Hash `json:"target"`
+ Message string `json:"message"`
+ Files []git.Hash `json:"files"`
+ }{}
+
+ err = json.Unmarshal(data, &aux)
+ if err != nil {
+ return err
+ }
+
+ op.OpBase = base
+ op.Target = aux.Target
+ op.Message = aux.Message
+ op.Files = aux.Files
+
+ return nil
+}
+
// Sign post method for gqlgen
func (op *EditCommentOperation) IsAuthored() {}
diff --git a/bug/op_edit_comment_test.go b/bug/op_edit_comment_test.go
index 7eee2fc1..dbdf341d 100644
--- a/bug/op_edit_comment_test.go
+++ b/bug/op_edit_comment_test.go
@@ -1,11 +1,12 @@
package bug
import (
+ "encoding/json"
"testing"
"time"
"github.com/MichaelMure/git-bug/identity"
- "gotest.tools/assert"
+ "github.com/stretchr/testify/assert"
)
func TestEdit(t *testing.T) {
@@ -49,3 +50,18 @@ func TestEdit(t *testing.T) {
assert.Equal(t, snapshot.Comments[0].Message, "create edited")
assert.Equal(t, snapshot.Comments[1].Message, "comment edited")
}
+
+func TestEditCommentSerialize(t *testing.T) {
+ var rene = identity.NewBare("René Descartes", "rene@descartes.fr")
+ unix := time.Now().Unix()
+ before := NewEditCommentOp(rene, unix, "target", "message", nil)
+
+ data, err := json.Marshal(before)
+ assert.NoError(t, err)
+
+ var after EditCommentOperation
+ err = json.Unmarshal(data, &after)
+ assert.NoError(t, err)
+
+ assert.Equal(t, before, &after)
+}
diff --git a/bug/op_label_change.go b/bug/op_label_change.go
index 5d0b6a78..b0dd2c33 100644
--- a/bug/op_label_change.go
+++ b/bug/op_label_change.go
@@ -1,6 +1,7 @@
package bug
import (
+ "encoding/json"
"fmt"
"sort"
@@ -15,8 +16,8 @@ var _ Operation = &LabelChangeOperation{}
// LabelChangeOperation define a Bug operation to add or remove labels
type LabelChangeOperation struct {
OpBase
- Added []Label `json:"added"`
- Removed []Label `json:"removed"`
+ Added []Label
+ Removed []Label
}
func (op *LabelChangeOperation) base() *OpBase {
@@ -99,6 +100,54 @@ func (op *LabelChangeOperation) Validate() error {
return nil
}
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *LabelChangeOperation) MarshalJSON() ([]byte, error) {
+ base, err := json.Marshal(op.OpBase)
+ if err != nil {
+ return nil, err
+ }
+
+ // revert back to a flat map to be able to add our own fields
+ var data map[string]interface{}
+ if err := json.Unmarshal(base, &data); err != nil {
+ return nil, err
+ }
+
+ data["added"] = op.Added
+ data["removed"] = op.Removed
+
+ return json.Marshal(data)
+}
+
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *LabelChangeOperation) UnmarshalJSON(data []byte) error {
+ // Unmarshal OpBase and the op separately
+
+ base := OpBase{}
+ err := json.Unmarshal(data, &base)
+ if err != nil {
+ return err
+ }
+
+ aux := struct {
+ Added []Label `json:"added"`
+ Removed []Label `json:"removed"`
+ }{}
+
+ err = json.Unmarshal(data, &aux)
+ if err != nil {
+ return err
+ }
+
+ op.OpBase = base
+ op.Added = aux.Added
+ op.Removed = aux.Removed
+
+ return nil
+}
+
// Sign post method for gqlgen
func (op *LabelChangeOperation) IsAuthored() {}
diff --git a/bug/op_label_change_test.go b/bug/op_label_change_test.go
new file mode 100644
index 00000000..f5550b72
--- /dev/null
+++ b/bug/op_label_change_test.go
@@ -0,0 +1,25 @@
+package bug
+
+import (
+ "encoding/json"
+ "testing"
+ "time"
+
+ "github.com/MichaelMure/git-bug/identity"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestLabelChangeSerialize(t *testing.T) {
+ var rene = identity.NewBare("René Descartes", "rene@descartes.fr")
+ unix := time.Now().Unix()
+ before := NewLabelChangeOperation(rene, unix, []Label{"added"}, []Label{"removed"})
+
+ data, err := json.Marshal(before)
+ assert.NoError(t, err)
+
+ var after LabelChangeOperation
+ err = json.Unmarshal(data, &after)
+ assert.NoError(t, err)
+
+ assert.Equal(t, before, &after)
+}
diff --git a/bug/op_noop.go b/bug/op_noop.go
index 410799b3..fbc112a8 100644
--- a/bug/op_noop.go
+++ b/bug/op_noop.go
@@ -1,6 +1,8 @@
package bug
import (
+ "encoding/json"
+
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/git"
)
@@ -30,6 +32,46 @@ func (op *NoOpOperation) Validate() error {
return opBaseValidate(op, NoOpOp)
}
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *NoOpOperation) MarshalJSON() ([]byte, error) {
+ base, err := json.Marshal(op.OpBase)
+ if err != nil {
+ return nil, err
+ }
+
+ // revert back to a flat map to be able to add our own fields
+ var data map[string]interface{}
+ if err := json.Unmarshal(base, &data); err != nil {
+ return nil, err
+ }
+
+ return json.Marshal(data)
+}
+
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *NoOpOperation) UnmarshalJSON(data []byte) error {
+ // Unmarshal OpBase and the op separately
+
+ base := OpBase{}
+ err := json.Unmarshal(data, &base)
+ if err != nil {
+ return err
+ }
+
+ aux := struct{}{}
+
+ err = json.Unmarshal(data, &aux)
+ if err != nil {
+ return err
+ }
+
+ op.OpBase = base
+
+ return nil
+}
+
// Sign post method for gqlgen
func (op *NoOpOperation) IsAuthored() {}
diff --git a/bug/op_noop_test.go b/bug/op_noop_test.go
new file mode 100644
index 00000000..385bc914
--- /dev/null
+++ b/bug/op_noop_test.go
@@ -0,0 +1,25 @@
+package bug
+
+import (
+ "encoding/json"
+ "testing"
+ "time"
+
+ "github.com/MichaelMure/git-bug/identity"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestNoopSerialize(t *testing.T) {
+ var rene = identity.NewBare("René Descartes", "rene@descartes.fr")
+ unix := time.Now().Unix()
+ before := NewNoOpOp(rene, unix)
+
+ data, err := json.Marshal(before)
+ assert.NoError(t, err)
+
+ var after NoOpOperation
+ err = json.Unmarshal(data, &after)
+ assert.NoError(t, err)
+
+ assert.Equal(t, before, &after)
+}
diff --git a/bug/op_set_metadata.go b/bug/op_set_metadata.go
index e18f1cb6..57b78667 100644
--- a/bug/op_set_metadata.go
+++ b/bug/op_set_metadata.go
@@ -1,6 +1,8 @@
package bug
import (
+ "encoding/json"
+
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/git"
)
@@ -9,8 +11,8 @@ var _ Operation = &SetMetadataOperation{}
type SetMetadataOperation struct {
OpBase
- Target git.Hash `json:"target"`
- NewMetadata map[string]string `json:"new_metadata"`
+ Target git.Hash
+ NewMetadata map[string]string
}
func (op *SetMetadataOperation) base() *OpBase {
@@ -56,6 +58,54 @@ func (op *SetMetadataOperation) Validate() error {
return nil
}
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *SetMetadataOperation) MarshalJSON() ([]byte, error) {
+ base, err := json.Marshal(op.OpBase)
+ if err != nil {
+ return nil, err
+ }
+
+ // revert back to a flat map to be able to add our own fields
+ var data map[string]interface{}
+ if err := json.Unmarshal(base, &data); err != nil {
+ return nil, err
+ }
+
+ data["target"] = op.Target
+ data["new_metadata"] = op.NewMetadata
+
+ return json.Marshal(data)
+}
+
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *SetMetadataOperation) UnmarshalJSON(data []byte) error {
+ // Unmarshal OpBase and the op separately
+
+ base := OpBase{}
+ err := json.Unmarshal(data, &base)
+ if err != nil {
+ return err
+ }
+
+ aux := struct {
+ Target git.Hash `json:"target"`
+ NewMetadata map[string]string `json:"new_metadata"`
+ }{}
+
+ err = json.Unmarshal(data, &aux)
+ if err != nil {
+ return err
+ }
+
+ op.OpBase = base
+ op.Target = aux.Target
+ op.NewMetadata = aux.NewMetadata
+
+ return nil
+}
+
// Sign post method for gqlgen
func (op *SetMetadataOperation) IsAuthored() {}
diff --git a/bug/op_set_metadata_test.go b/bug/op_set_metadata_test.go
index 6e62c9a3..847164f3 100644
--- a/bug/op_set_metadata_test.go
+++ b/bug/op_set_metadata_test.go
@@ -1,6 +1,7 @@
package bug
import (
+ "encoding/json"
"testing"
"time"
@@ -94,3 +95,21 @@ func TestSetMetadata(t *testing.T) {
assert.Equal(t, commentMetadata["key2"], "value2")
assert.Equal(t, commentMetadata["key3"], "value3")
}
+
+func TestSetMetadataSerialize(t *testing.T) {
+ var rene = identity.NewBare("René Descartes", "rene@descartes.fr")
+ unix := time.Now().Unix()
+ before := NewSetMetadataOp(rene, unix, "message", map[string]string{
+ "key1": "value1",
+ "key2": "value2",
+ })
+
+ data, err := json.Marshal(before)
+ assert.NoError(t, err)
+
+ var after SetMetadataOperation
+ err = json.Unmarshal(data, &after)
+ assert.NoError(t, err)
+
+ assert.Equal(t, before, &after)
+}
diff --git a/bug/op_set_status.go b/bug/op_set_status.go
index 9fc64e52..6deb1675 100644
--- a/bug/op_set_status.go
+++ b/bug/op_set_status.go
@@ -1,6 +1,8 @@
package bug
import (
+ "encoding/json"
+
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/util/git"
"github.com/pkg/errors"
@@ -11,7 +13,7 @@ var _ Operation = &SetStatusOperation{}
// SetStatusOperation will change the status of a bug
type SetStatusOperation struct {
OpBase
- Status Status `json:"status"`
+ Status Status
}
func (op *SetStatusOperation) base() *OpBase {
@@ -54,6 +56,51 @@ func (op *SetStatusOperation) Validate() error {
return nil
}
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *SetStatusOperation) MarshalJSON() ([]byte, error) {
+ base, err := json.Marshal(op.OpBase)
+ if err != nil {
+ return nil, err
+ }
+
+ // revert back to a flat map to be able to add our own fields
+ var data map[string]interface{}
+ if err := json.Unmarshal(base, &data); err != nil {
+ return nil, err
+ }
+
+ data["status"] = op.Status
+
+ return json.Marshal(data)
+}
+
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *SetStatusOperation) UnmarshalJSON(data []byte) error {
+ // Unmarshal OpBase and the op separately
+
+ base := OpBase{}
+ err := json.Unmarshal(data, &base)
+ if err != nil {
+ return err
+ }
+
+ aux := struct {
+ Status Status `json:"status"`
+ }{}
+
+ err = json.Unmarshal(data, &aux)
+ if err != nil {
+ return err
+ }
+
+ op.OpBase = base
+ op.Status = aux.Status
+
+ return nil
+}
+
// Sign post method for gqlgen
func (op *SetStatusOperation) IsAuthored() {}
diff --git a/bug/op_set_status_test.go b/bug/op_set_status_test.go
new file mode 100644
index 00000000..2506b947
--- /dev/null
+++ b/bug/op_set_status_test.go
@@ -0,0 +1,25 @@
+package bug
+
+import (
+ "encoding/json"
+ "testing"
+ "time"
+
+ "github.com/MichaelMure/git-bug/identity"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSetStatusSerialize(t *testing.T) {
+ var rene = identity.NewBare("René Descartes", "rene@descartes.fr")
+ unix := time.Now().Unix()
+ before := NewSetStatusOp(rene, unix, ClosedStatus)
+
+ data, err := json.Marshal(before)
+ assert.NoError(t, err)
+
+ var after SetStatusOperation
+ err = json.Unmarshal(data, &after)
+ assert.NoError(t, err)
+
+ assert.Equal(t, before, &after)
+}
diff --git a/bug/op_set_title.go b/bug/op_set_title.go
index 3b253c06..ae6484c6 100644
--- a/bug/op_set_title.go
+++ b/bug/op_set_title.go
@@ -1,6 +1,7 @@
package bug
import (
+ "encoding/json"
"fmt"
"strings"
@@ -15,8 +16,8 @@ var _ Operation = &SetTitleOperation{}
// SetTitleOperation will change the title of a bug
type SetTitleOperation struct {
OpBase
- Title string `json:"title"`
- Was string `json:"was"`
+ Title string
+ Was string
}
func (op *SetTitleOperation) base() *OpBase {
@@ -76,6 +77,54 @@ func (op *SetTitleOperation) Validate() error {
return nil
}
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *SetTitleOperation) MarshalJSON() ([]byte, error) {
+ base, err := json.Marshal(op.OpBase)
+ if err != nil {
+ return nil, err
+ }
+
+ // revert back to a flat map to be able to add our own fields
+ var data map[string]interface{}
+ if err := json.Unmarshal(base, &data); err != nil {
+ return nil, err
+ }
+
+ data["title"] = op.Title
+ data["was"] = op.Was
+
+ return json.Marshal(data)
+}
+
+// Workaround to avoid the inner OpBase.MarshalJSON overriding the outer op
+// MarshalJSON
+func (op *SetTitleOperation) UnmarshalJSON(data []byte) error {
+ // Unmarshal OpBase and the op separately
+
+ base := OpBase{}
+ err := json.Unmarshal(data, &base)
+ if err != nil {
+ return err
+ }
+
+ aux := struct {
+ Title string `json:"title"`
+ Was string `json:"was"`
+ }{}
+
+ err = json.Unmarshal(data, &aux)
+ if err != nil {
+ return err
+ }
+
+ op.OpBase = base
+ op.Title = aux.Title
+ op.Was = aux.Was
+
+ return nil
+}
+
// Sign post method for gqlgen
func (op *SetTitleOperation) IsAuthored() {}
diff --git a/bug/op_set_title_test.go b/bug/op_set_title_test.go
new file mode 100644
index 00000000..1f730596
--- /dev/null
+++ b/bug/op_set_title_test.go
@@ -0,0 +1,25 @@
+package bug
+
+import (
+ "encoding/json"
+ "testing"
+ "time"
+
+ "github.com/MichaelMure/git-bug/identity"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSetTitleSerialize(t *testing.T) {
+ var rene = identity.NewBare("René Descartes", "rene@descartes.fr")
+ unix := time.Now().Unix()
+ before := NewSetTitleOp(rene, unix, "title", "was")
+
+ data, err := json.Marshal(before)
+ assert.NoError(t, err)
+
+ var after SetTitleOperation
+ err = json.Unmarshal(data, &after)
+ assert.NoError(t, err)
+
+ assert.Equal(t, before, &after)
+}
diff --git a/bug/operation.go b/bug/operation.go
index 8dec5644..cc5b0007 100644
--- a/bug/operation.go
+++ b/bug/operation.go
@@ -76,8 +76,6 @@ func hashOperation(op Operation) (git.Hash, error) {
return base.hash, nil
}
-// TODO: serialization with identity
-
// OpBase implement the common code for all operations
type OpBase struct {
OperationType OperationType
@@ -100,28 +98,40 @@ func newOpBase(opType OperationType, author identity.Interface, unixTime int64)
}
}
-type opBaseJson struct {
- OperationType OperationType `json:"type"`
- UnixTime int64 `json:"timestamp"`
- Metadata map[string]string `json:"metadata,omitempty"`
-}
-
-func (op *OpBase) MarshalJSON() ([]byte, error) {
- return json.Marshal(opBaseJson{
+func (op OpBase) MarshalJSON() ([]byte, error) {
+ return json.Marshal(struct {
+ OperationType OperationType `json:"type"`
+ Author identity.Interface `json:"author"`
+ UnixTime int64 `json:"timestamp"`
+ Metadata map[string]string `json:"metadata,omitempty"`
+ }{
OperationType: op.OperationType,
+ Author: op.Author,
UnixTime: op.UnixTime,
Metadata: op.Metadata,
})
}
func (op *OpBase) UnmarshalJSON(data []byte) error {
- aux := opBaseJson{}
+ aux := struct {
+ OperationType OperationType `json:"type"`
+ Author json.RawMessage `json:"author"`
+ UnixTime int64 `json:"timestamp"`
+ Metadata map[string]string `json:"metadata,omitempty"`
+ }{}
if err := json.Unmarshal(data, &aux); err != nil {
return err
}
+ // delegate the decoding of the identity
+ author, err := identity.UnmarshalJSON(aux.Author)
+ if err != nil {
+ return err
+ }
+
op.OperationType = aux.OperationType
+ op.Author = author
op.UnixTime = aux.UnixTime
op.Metadata = aux.Metadata
@@ -149,10 +159,6 @@ func opBaseValidate(op Operation, opType OperationType) error {
return fmt.Errorf("incorrect operation type (expected: %v, actual: %v)", opType, op.base().OperationType)
}
- if _, err := op.Hash(); err != nil {
- return errors.Wrap(err, "op is not serializable")
- }
-
if op.GetUnixTime() == 0 {
return fmt.Errorf("time not set")
}
diff --git a/bug/operation_iterator_test.go b/bug/operation_iterator_test.go
index b8e1bf09..e1aa8911 100644
--- a/bug/operation_iterator_test.go
+++ b/bug/operation_iterator_test.go
@@ -3,6 +3,8 @@ package bug
import (
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
+ "github.com/stretchr/testify/assert"
+
"testing"
"time"
)
@@ -29,13 +31,15 @@ func TestOpIterator(t *testing.T) {
bug1.Append(addCommentOp)
bug1.Append(setStatusOp)
bug1.Append(labelChangeOp)
- bug1.Commit(mockRepo)
+ err := bug1.Commit(mockRepo)
+ assert.NoError(t, err)
// second pack
bug1.Append(setTitleOp)
bug1.Append(setTitleOp)
bug1.Append(setTitleOp)
- bug1.Commit(mockRepo)
+ err = bug1.Commit(mockRepo)
+ assert.NoError(t, err)
// staging
bug1.Append(setTitleOp)
diff --git a/bug/operation_pack.go b/bug/operation_pack.go
index fc395d90..18b2a478 100644
--- a/bug/operation_pack.go
+++ b/bug/operation_pack.go
@@ -139,6 +139,14 @@ func (opp *OperationPack) Validate() error {
// Write will serialize and store the OperationPack as a git blob and return
// its hash
func (opp *OperationPack) Write(repo repository.Repo) (git.Hash, error) {
+ // First, make sure that all the identities are properly Commit as well
+ for _, op := range opp.Operations {
+ err := op.base().Author.Commit(repo)
+ if err != nil {
+ return "", err
+ }
+ }
+
data, err := json.Marshal(opp)
if err != nil {