aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2020-11-08 19:15:06 +0100
committerMichael Muré <batolettre@gmail.com>2021-02-14 12:17:44 +0100
commit7163b2283b4542a4d4abfe9a71963f122322bde7 (patch)
tree93a52a65c5ba1fd3e4112b2aa53c2c438bb5400c
parent5ae8a132772385c903a62de2ceec02a97f108a01 (diff)
downloadgit-bug-7163b2283b4542a4d4abfe9a71963f122322bde7.tar.gz
bug: Id from first operation data, not git + remove root link
-rw-r--r--bug/bug.go233
-rw-r--r--bug/bug_actions_test.go20
-rw-r--r--bug/bug_test.go28
-rw-r--r--bug/git_tree.go84
-rw-r--r--bug/op_add_comment_test.go4
-rw-r--r--bug/op_create.go26
-rw-r--r--bug/op_create_test.go26
-rw-r--r--bug/op_edit_comment_test.go59
-rw-r--r--bug/op_label_change_test.go14
-rw-r--r--bug/op_noop_test.go4
-rw-r--r--bug/op_set_metadata_test.go51
-rw-r--r--bug/op_set_status_test.go14
-rw-r--r--bug/op_set_title_test.go14
-rw-r--r--bug/operation_iterator_test.go11
-rw-r--r--bug/operation_pack.go5
-rw-r--r--bug/operation_pack_test.go15
-rw-r--r--bug/operation_test.go32
-rw-r--r--entity/refs.go4
-rw-r--r--identity/identity.go4
19 files changed, 341 insertions, 307 deletions
diff --git a/bug/bug.go b/bug/bug.go
index f6c35a2d..e67920f9 100644
--- a/bug/bug.go
+++ b/bug/bug.go
@@ -4,7 +4,6 @@ package bug
import (
"encoding/json"
"fmt"
- "strings"
"github.com/pkg/errors"
@@ -18,7 +17,6 @@ const bugsRefPattern = "refs/bugs/"
const bugsRemoteRefPattern = "refs/remotes/%s/bugs/"
const opsEntryName = "ops"
-const rootEntryName = "root"
const mediaEntryName = "media"
const createClockEntryPrefix = "create-clock-"
@@ -57,7 +55,6 @@ type Bug struct {
id entity.Id
lastCommit repository.Hash
- rootPack repository.Hash
// all the committed operations
packs []OperationPack
@@ -71,7 +68,7 @@ type Bug struct {
func NewBug() *Bug {
// No id yet
// No logical clock yet
- return &Bug{}
+ return &Bug{id: entity.UnsetId}
}
// ReadLocal will read a local bug from its hash
@@ -100,122 +97,77 @@ func ReadRemoteWithResolver(repo repository.ClockedRepo, identityResolver identi
// read will read and parse a Bug from git
func read(repo repository.ClockedRepo, identityResolver identity.Resolver, ref string) (*Bug, error) {
- refSplit := strings.Split(ref, "/")
- id := entity.Id(refSplit[len(refSplit)-1])
+ id := entity.RefToId(ref)
if err := id.Validate(); err != nil {
return nil, errors.Wrap(err, "invalid ref ")
}
hashes, err := repo.ListCommits(ref)
-
- // TODO: this is not perfect, it might be a command invoke error
if err != nil {
return nil, ErrBugNotExist
}
+ if len(hashes) == 0 {
+ return nil, fmt.Errorf("empty bug")
+ }
bug := Bug{
- id: id,
- editTime: 0,
+ id: id,
}
// Load each OperationPack
for _, hash := range hashes {
- entries, err := repo.ReadTree(hash)
+ tree, err := readTree(repo, hash)
if err != nil {
- return nil, errors.Wrap(err, "can't list git tree entries")
- }
-
- bug.lastCommit = hash
-
- var opsEntry repository.TreeEntry
- opsFound := false
- var rootEntry repository.TreeEntry
- rootFound := false
- var createTime uint64
- var editTime uint64
-
- for _, entry := range entries {
- if entry.Name == opsEntryName {
- opsEntry = entry
- opsFound = true
- continue
- }
- if entry.Name == rootEntryName {
- rootEntry = entry
- rootFound = true
- }
- if strings.HasPrefix(entry.Name, createClockEntryPrefix) {
- n, err := fmt.Sscanf(entry.Name, createClockEntryPattern, &createTime)
- if err != nil {
- return nil, errors.Wrap(err, "can't read create lamport time")
- }
- if n != 1 {
- return nil, fmt.Errorf("could not parse create time lamport value")
- }
- }
- if strings.HasPrefix(entry.Name, editClockEntryPrefix) {
- n, err := fmt.Sscanf(entry.Name, editClockEntryPattern, &editTime)
- if err != nil {
- return nil, errors.Wrap(err, "can't read edit lamport time")
- }
- if n != 1 {
- return nil, fmt.Errorf("could not parse edit time lamport value")
- }
- }
- }
-
- if !opsFound {
- return nil, errors.New("invalid tree, missing the ops entry")
- }
- if !rootFound {
- return nil, errors.New("invalid tree, missing the root entry")
- }
-
- if bug.rootPack == "" {
- bug.rootPack = rootEntry.Hash
- bug.createTime = lamport.Time(createTime)
+ return nil, err
}
// Due to rebase, edit Lamport time are not necessarily ordered
- if editTime > uint64(bug.editTime) {
- bug.editTime = lamport.Time(editTime)
+ if tree.editTime > bug.editTime {
+ bug.editTime = tree.editTime
}
// Update the clocks
- createClock, err := repo.GetOrCreateClock(creationClockName)
+ err = repo.Witness(creationClockName, bug.createTime)
if err != nil {
- return nil, err
- }
- if err := createClock.Witness(bug.createTime); err != nil {
return nil, errors.Wrap(err, "failed to update create lamport clock")
}
- editClock, err := repo.GetOrCreateClock(editClockName)
+ err = repo.Witness(editClockName, bug.editTime)
if err != nil {
- return nil, err
- }
- if err := editClock.Witness(bug.editTime); err != nil {
return nil, errors.Wrap(err, "failed to update edit lamport clock")
}
- data, err := repo.ReadData(opsEntry.Hash)
+ data, err := repo.ReadData(tree.opsEntry.Hash)
if err != nil {
return nil, errors.Wrap(err, "failed to read git blob data")
}
opp := &OperationPack{}
err = json.Unmarshal(data, &opp)
-
if err != nil {
return nil, errors.Wrap(err, "failed to decode OperationPack json")
}
// tag the pack with the commit hash
opp.commitHash = hash
+ bug.lastCommit = hash
+
+ // if it's the first OperationPack read
+ if len(bug.packs) == 0 {
+ bug.createTime = tree.createTime
+ }
bug.packs = append(bug.packs, *opp)
}
+ // Bug Id is the Id of the first operation
+ if len(bug.packs[0].Operations) == 0 {
+ return nil, fmt.Errorf("first OperationPack is empty")
+ }
+ if bug.id != bug.packs[0].Operations[0].Id() {
+ return nil, fmt.Errorf("bug ID doesn't match the first operation ID")
+ }
+
// Make sure that the identities are properly loaded
err = bug.EnsureIdentities(identityResolver)
if err != nil {
@@ -367,8 +319,8 @@ func (bug *Bug) Validate() error {
return fmt.Errorf("first operation should be a Create op")
}
- // The bug Id should be the hash of the first commit
- if len(bug.packs) > 0 && string(bug.packs[0].commitHash) != bug.id.String() {
+ // The bug Id should be the id of the first operation
+ if bug.FirstOp().Id() != bug.id {
return fmt.Errorf("bug id should be the first commit hash")
}
@@ -396,12 +348,17 @@ func (bug *Bug) Validate() error {
// Append an operation into the staging area, to be committed later
func (bug *Bug) Append(op Operation) {
+ if len(bug.packs) == 0 && len(bug.staging.Operations) == 0 {
+ if op.base().OperationType != CreateOp {
+ panic("first operation should be a Create")
+ }
+ bug.id = op.Id()
+ }
bug.staging.Append(op)
}
// Commit write the staging area in Git and move the operations to the packs
func (bug *Bug) Commit(repo repository.ClockedRepo) error {
-
if !bug.NeedCommit() {
return fmt.Errorf("can't commit a bug with no pending operation")
}
@@ -410,38 +367,29 @@ func (bug *Bug) Commit(repo repository.ClockedRepo) error {
return errors.Wrap(err, "can't commit a bug with invalid data")
}
- // Write the Ops as a Git blob containing the serialized array
- hash, err := bug.staging.Write(repo)
+ // update clocks
+ var err error
+ bug.editTime, err = repo.Increment(editClockName)
if err != nil {
return err
}
+ if bug.lastCommit == "" {
+ bug.createTime, err = repo.Increment(creationClockName)
+ if err != nil {
+ return err
+ }
+ }
- if bug.rootPack == "" {
- bug.rootPack = hash
+ // Write the Ops as a Git blob containing the serialized array
+ hash, err := bug.staging.Write(repo)
+ if err != nil {
+ return err
}
// Make a Git tree referencing this blob
tree := []repository.TreeEntry{
// the last pack of ops
{ObjectType: repository.Blob, Hash: hash, Name: opsEntryName},
- // always the first pack of ops (might be the same)
- {ObjectType: repository.Blob, Hash: bug.rootPack, Name: rootEntryName},
- }
-
- // Reference, if any, all the files required by the ops
- // Git will check that they actually exist in the storage and will make sure
- // to push/pull them as needed.
- mediaTree := makeMediaTree(bug.staging)
- if len(mediaTree) > 0 {
- mediaTreeHash, err := repo.StoreTree(mediaTree)
- if err != nil {
- return err
- }
- tree = append(tree, repository.TreeEntry{
- ObjectType: repository.Tree,
- Hash: mediaTreeHash,
- Name: mediaEntryName,
- })
}
// Store the logical clocks as well
@@ -454,31 +402,12 @@ func (bug *Bug) Commit(repo repository.ClockedRepo) error {
if err != nil {
return err
}
-
- editClock, err := repo.GetOrCreateClock(editClockName)
- if err != nil {
- return err
- }
- bug.editTime, err = editClock.Increment()
- if err != nil {
- return err
- }
-
tree = append(tree, repository.TreeEntry{
ObjectType: repository.Blob,
Hash: emptyBlobHash,
Name: fmt.Sprintf(editClockEntryPattern, bug.editTime),
})
if bug.lastCommit == "" {
- createClock, err := repo.GetOrCreateClock(creationClockName)
- if err != nil {
- return err
- }
- bug.createTime, err = createClock.Increment()
- if err != nil {
- return err
- }
-
tree = append(tree, repository.TreeEntry{
ObjectType: repository.Blob,
Hash: emptyBlobHash,
@@ -486,6 +415,22 @@ func (bug *Bug) Commit(repo repository.ClockedRepo) error {
})
}
+ // Reference, if any, all the files required by the ops
+ // Git will check that they actually exist in the storage and will make sure
+ // to push/pull them as needed.
+ mediaTree := makeMediaTree(bug.staging)
+ if len(mediaTree) > 0 {
+ mediaTreeHash, err := repo.StoreTree(mediaTree)
+ if err != nil {
+ return err
+ }
+ tree = append(tree, repository.TreeEntry{
+ ObjectType: repository.Tree,
+ Hash: mediaTreeHash,
+ Name: mediaEntryName,
+ })
+ }
+
// Store the tree
hash, err = repo.StoreTree(tree)
if err != nil {
@@ -498,33 +443,25 @@ func (bug *Bug) Commit(repo repository.ClockedRepo) error {
} else {
hash, err = repo.StoreCommit(hash)
}
-
if err != nil {
return err
}
bug.lastCommit = hash
+ bug.staging.commitHash = hash
+ bug.packs = append(bug.packs, bug.staging)
+ bug.staging = OperationPack{}
- // if it was the first commit, use the commit hash as bug id
- if bug.id == "" {
- bug.id = entity.Id(hash)
+ // if it was the first commit, use the Id of the first op (create)
+ if bug.id == "" || bug.id == entity.UnsetId {
+ bug.id = bug.packs[0].Operations[0].Id()
}
// Create or update the Git reference for this bug
// When pushing later, the remote will ensure that this ref update
// is fast-forward, that is no data has been overwritten
ref := fmt.Sprintf("%s%s", bugsRefPattern, bug.id)
- err = repo.UpdateRef(ref, hash)
-
- if err != nil {
- return err
- }
-
- bug.staging.commitHash = hash
- bug.packs = append(bug.packs, bug.staging)
- bug.staging = OperationPack{}
-
- return nil
+ return repo.UpdateRef(ref, hash)
}
func (bug *Bug) CommitAsNeeded(repo repository.ClockedRepo) error {
@@ -538,30 +475,6 @@ func (bug *Bug) NeedCommit() bool {
return !bug.staging.IsEmpty()
}
-func makeMediaTree(pack OperationPack) []repository.TreeEntry {
- var tree []repository.TreeEntry
- counter := 0
- added := make(map[repository.Hash]interface{})
-
- for _, ops := range pack.Operations {
- for _, file := range ops.GetFiles() {
- if _, has := added[file]; !has {
- tree = append(tree, repository.TreeEntry{
- ObjectType: repository.Blob,
- Hash: file,
- // The name is not important here, we only need to
- // reference the blob.
- Name: fmt.Sprintf("file%d", counter),
- })
- counter++
- added[file] = struct{}{}
- }
- }
- }
-
- return tree
-}
-
// Merge a different version of the same bug by rebasing operations of this bug
// that are not present in the other on top of the chain of operations of the
// other version.
@@ -657,9 +570,9 @@ func (bug *Bug) Merge(repo repository.Repo, other Interface) (bool, error) {
// Id return the Bug identifier
func (bug *Bug) Id() entity.Id {
- if bug.id == "" {
+ if bug.id == "" || bug.id == entity.UnsetId {
// simply panic as it would be a coding error
- // (using an id of a bug not stored yet)
+ // (using an id of a bug without operation yet)
panic("no id yet")
}
return bug.id
diff --git a/bug/bug_actions_test.go b/bug/bug_actions_test.go
index df35a5e5..7a9673d6 100644
--- a/bug/bug_actions_test.go
+++ b/bug/bug_actions_test.go
@@ -15,8 +15,9 @@ func TestPushPull(t *testing.T) {
repoA, repoB, remote := repository.SetupReposAndRemote()
defer repository.CleanupTestRepos(repoA, repoB, remote)
- reneA := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := reneA.Commit(repoA)
+ reneA, err := identity.NewIdentity(repoA, "René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+ err = reneA.Commit(repoA)
require.NoError(t, err)
bug1, _, err := Create(reneA, time.Now().Unix(), "bug1", "message")
@@ -92,8 +93,9 @@ func _RebaseTheirs(t testing.TB) {
repoA, repoB, remote := repository.SetupReposAndRemote()
defer repository.CleanupTestRepos(repoA, repoB, remote)
- reneA := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := reneA.Commit(repoA)
+ reneA, err := identity.NewIdentity(repoA, "René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+ err = reneA.Commit(repoA)
require.NoError(t, err)
bug1, _, err := Create(reneA, time.Now().Unix(), "bug1", "message")
@@ -172,8 +174,9 @@ func _RebaseOurs(t testing.TB) {
repoA, repoB, remote := repository.SetupReposAndRemote()
defer repository.CleanupTestRepos(repoA, repoB, remote)
- reneA := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := reneA.Commit(repoA)
+ reneA, err := identity.NewIdentity(repoA, "René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+ err = reneA.Commit(repoA)
require.NoError(t, err)
bug1, _, err := Create(reneA, time.Now().Unix(), "bug1", "message")
@@ -263,8 +266,9 @@ func _RebaseConflict(t testing.TB) {
repoA, repoB, remote := repository.SetupReposAndRemote()
defer repository.CleanupTestRepos(repoA, repoB, remote)
- reneA := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := reneA.Commit(repoA)
+ reneA, err := identity.NewIdentity(repoA, "René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+ err = reneA.Commit(repoA)
require.NoError(t, err)
bug1, _, err := Create(reneA, time.Now().Unix(), "bug1", "message")
diff --git a/bug/bug_test.go b/bug/bug_test.go
index 047fe386..a8987ac1 100644
--- a/bug/bug_test.go
+++ b/bug/bug_test.go
@@ -12,19 +12,20 @@ import (
)
func TestBugId(t *testing.T) {
- mockRepo := repository.NewMockRepo()
+ repo := repository.NewMockRepo()
bug1 := NewBug()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(mockRepo)
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+ err = rene.Commit(repo)
require.NoError(t, err)
createOp := NewCreateOp(rene, time.Now().Unix(), "title", "message", nil)
bug1.Append(createOp)
- err = bug1.Commit(mockRepo)
+ err = bug1.Commit(repo)
if err != nil {
t.Fatal(err)
@@ -34,12 +35,13 @@ func TestBugId(t *testing.T) {
}
func TestBugValidity(t *testing.T) {
- mockRepo := repository.NewMockRepo()
+ repo := repository.NewMockRepo()
bug1 := NewBug()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(mockRepo)
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+ err = rene.Commit(repo)
require.NoError(t, err)
createOp := NewCreateOp(rene, time.Now().Unix(), "title", "message", nil)
@@ -54,7 +56,7 @@ func TestBugValidity(t *testing.T) {
t.Fatal("Bug with just a CreateOp should be valid")
}
- err = bug1.Commit(mockRepo)
+ err = bug1.Commit(repo)
if err != nil {
t.Fatal(err)
}
@@ -65,7 +67,7 @@ func TestBugValidity(t *testing.T) {
t.Fatal("Bug with multiple CreateOp should be invalid")
}
- err = bug1.Commit(mockRepo)
+ err = bug1.Commit(repo)
if err == nil {
t.Fatal("Invalid bug should not commit")
}
@@ -76,8 +78,9 @@ func TestBugCommitLoad(t *testing.T) {
bug1 := NewBug()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(repo)
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+ err = rene.Commit(repo)
require.NoError(t, err)
createOp := NewCreateOp(rene, time.Now().Unix(), "title", "message", nil)
@@ -137,7 +140,8 @@ func TestBugRemove(t *testing.T) {
require.NoError(t, err)
// generate a bunch of bugs
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
err = rene.Commit(repo)
require.NoError(t, err)
diff --git a/bug/git_tree.go b/bug/git_tree.go
new file mode 100644
index 00000000..a5583bda
--- /dev/null
+++ b/bug/git_tree.go
@@ -0,0 +1,84 @@
+package bug
+
+import (
+ "fmt"
+ "strings"
+
+ "github.com/pkg/errors"
+
+ "github.com/MichaelMure/git-bug/repository"
+ "github.com/MichaelMure/git-bug/util/lamport"
+)
+
+type gitTree struct {
+ opsEntry repository.TreeEntry
+ createTime lamport.Time
+ editTime lamport.Time
+}
+
+func readTree(repo repository.RepoData, hash repository.Hash) (*gitTree, error) {
+ tree := &gitTree{}
+
+ entries, err := repo.ReadTree(hash)
+ if err != nil {
+ return nil, errors.Wrap(err, "can't list git tree entries")
+ }
+
+ opsFound := false
+
+ for _, entry := range entries {
+ if entry.Name == opsEntryName {
+ tree.opsEntry = entry
+ opsFound = true
+ continue
+ }
+ if strings.HasPrefix(entry.Name, createClockEntryPrefix) {
+ n, err := fmt.Sscanf(entry.Name, createClockEntryPattern, &tree.createTime)
+ if err != nil {
+ return nil, errors.Wrap(err, "can't read create lamport time")
+ }
+ if n != 1 {
+ return nil, fmt.Errorf("could not parse create time lamport value")
+ }
+ }
+ if strings.HasPrefix(entry.Name, editClockEntryPrefix) {
+ n, err := fmt.Sscanf(entry.Name, editClockEntryPattern, &tree.editTime)
+ if err != nil {
+ return nil, errors.Wrap(err, "can't read edit lamport time")
+ }
+ if n != 1 {
+ return nil, fmt.Errorf("could not parse edit time lamport value")
+ }
+ }
+ }
+
+ if !opsFound {
+ return nil, errors.New("invalid tree, missing the ops entry")
+ }
+
+ return tree, nil
+}
+
+func makeMediaTree(pack OperationPack) []repository.TreeEntry {
+ var tree []repository.TreeEntry
+ counter := 0
+ added := make(map[repository.Hash]interface{})
+
+ for _, ops := range pack.Operations {
+ for _, file := range ops.GetFiles() {
+ if _, has := added[file]; !has {
+ tree = append(tree, repository.TreeEntry{
+ ObjectType: repository.Blob,
+ Hash: file,
+ // The name is not important here, we only need to
+ // reference the blob.
+ Name: fmt.Sprintf("file%d", counter),
+ })
+ counter++
+ added[file] = struct{}{}
+ }
+ }
+ }
+
+ return tree
+}
diff --git a/bug/op_add_comment_test.go b/bug/op_add_comment_test.go
index 3f9d02f1..3b41d62d 100644
--- a/bug/op_add_comment_test.go
+++ b/bug/op_add_comment_test.go
@@ -14,8 +14,8 @@ import (
func TestAddCommentSerialize(t *testing.T) {
repo := repository.NewMockRepo()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(repo)
+
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
require.NoError(t, err)
unix := time.Now().Unix()
diff --git a/bug/op_create.go b/bug/op_create.go
index 9bb40d35..3c8ce658 100644
--- a/bug/op_create.go
+++ b/bug/op_create.go
@@ -1,6 +1,7 @@
package bug
import (
+ "crypto/rand"
"encoding/json"
"fmt"
"strings"
@@ -17,6 +18,10 @@ var _ Operation = &CreateOperation{}
// CreateOperation define the initial creation of a bug
type CreateOperation struct {
OpBase
+ // mandatory random bytes to ensure a better randomness of the data of the first
+ // operation of a bug, used to later generate the ID
+ // len(Nonce) should be > 20 and < 64 bytes
+ Nonce []byte `json:"nonce"`
Title string `json:"title"`
Message string `json:"message"`
Files []repository.Hash `json:"files"`
@@ -66,14 +71,19 @@ func (op *CreateOperation) Validate() error {
return err
}
+ if len(op.Nonce) > 64 {
+ return fmt.Errorf("create nonce is too big")
+ }
+ if len(op.Nonce) < 20 {
+ return fmt.Errorf("create nonce is too small")
+ }
+
if text.Empty(op.Title) {
return fmt.Errorf("title is empty")
}
-
if strings.Contains(op.Title, "\n") {
return fmt.Errorf("title should be a single line")
}
-
if !text.Safe(op.Title) {
return fmt.Errorf("title is not fully printable")
}
@@ -98,6 +108,7 @@ func (op *CreateOperation) UnmarshalJSON(data []byte) error {
}
aux := struct {
+ Nonce []byte `json:"nonce"`
Title string `json:"title"`
Message string `json:"message"`
Files []repository.Hash `json:"files"`
@@ -109,6 +120,7 @@ func (op *CreateOperation) UnmarshalJSON(data []byte) error {
}
op.OpBase = base
+ op.Nonce = aux.Nonce
op.Title = aux.Title
op.Message = aux.Message
op.Files = aux.Files
@@ -119,9 +131,19 @@ func (op *CreateOperation) UnmarshalJSON(data []byte) error {
// Sign post method for gqlgen
func (op *CreateOperation) IsAuthored() {}
+func makeNonce(len int) []byte {
+ result := make([]byte, len)
+ _, err := rand.Read(result)
+ if err != nil {
+ panic(err)
+ }
+ return result
+}
+
func NewCreateOp(author identity.Interface, unixTime int64, title, message string, files []repository.Hash) *CreateOperation {
return &CreateOperation{
OpBase: newOpBase(CreateOp, author, unixTime),
+ Nonce: makeNonce(20),
Title: title,
Message: message,
Files: files,
diff --git a/bug/op_create_test.go b/bug/op_create_test.go
index 2d28a208..533aec2e 100644
--- a/bug/op_create_test.go
+++ b/bug/op_create_test.go
@@ -5,17 +5,21 @@ import (
"testing"
"time"
+ "github.com/stretchr/testify/require"
+
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
"github.com/MichaelMure/git-bug/util/timestamp"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
)
func TestCreate(t *testing.T) {
snapshot := Snapshot{}
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
+ repo := repository.NewMockRepoClock()
+
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+
unix := time.Now().Unix()
create := NewCreateOp(rene, unix, "title", "message", nil)
@@ -23,7 +27,7 @@ func TestCreate(t *testing.T) {
create.Apply(&snapshot)
id := create.Id()
- assert.NoError(t, id.Validate())
+ require.NoError(t, id.Validate())
comment := Comment{
id: id,
@@ -48,31 +52,31 @@ func TestCreate(t *testing.T) {
},
}
- assert.Equal(t, expected, snapshot)
+ require.Equal(t, expected, snapshot)
}
func TestCreateSerialize(t *testing.T) {
repo := repository.NewMockRepo()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(repo)
+
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
require.NoError(t, err)
unix := time.Now().Unix()
before := NewCreateOp(rene, unix, "title", "message", nil)
data, err := json.Marshal(before)
- assert.NoError(t, err)
+ require.NoError(t, err)
var after CreateOperation
err = json.Unmarshal(data, &after)
- assert.NoError(t, err)
+ require.NoError(t, err)
// enforce creating the ID
before.Id()
// Replace the identity stub with the real thing
- assert.Equal(t, rene.Id(), after.base().Author.Id())
+ require.Equal(t, rene.Id(), after.base().Author.Id())
after.Author = rene
- assert.Equal(t, before, &after)
+ require.Equal(t, before, &after)
}
diff --git a/bug/op_edit_comment_test.go b/bug/op_edit_comment_test.go
index 263111f9..92ee7539 100644
--- a/bug/op_edit_comment_test.go
+++ b/bug/op_edit_comment_test.go
@@ -5,7 +5,6 @@ import (
"testing"
"time"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/MichaelMure/git-bug/identity"
@@ -16,8 +15,8 @@ func TestEdit(t *testing.T) {
snapshot := Snapshot{}
repo := repository.NewMockRepo()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(repo)
+
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
require.NoError(t, err)
unix := time.Now().Unix()
@@ -47,59 +46,59 @@ func TestEdit(t *testing.T) {
edit := NewEditCommentOp(rene, unix, id1, "create edited", nil)
edit.Apply(&snapshot)
- assert.Equal(t, len(snapshot.Timeline), 4)
- assert.Equal(t, len(snapshot.Timeline[0].(*CreateTimelineItem).History), 2)
- assert.Equal(t, len(snapshot.Timeline[1].(*AddCommentTimelineItem).History), 1)
- assert.Equal(t, len(snapshot.Timeline[3].(*AddCommentTimelineItem).History), 1)
- assert.Equal(t, snapshot.Comments[0].Message, "create edited")
- assert.Equal(t, snapshot.Comments[1].Message, "comment 1")
- assert.Equal(t, snapshot.Comments[2].Message, "comment 2")
+ require.Equal(t, len(snapshot.Timeline), 4)
+ require.Equal(t, len(snapshot.Timeline[0].(*CreateTimelineItem).History), 2)
+ require.Equal(t, len(snapshot.Timeline[1].(*AddCommentTimelineItem).History), 1)
+ require.Equal(t, len(snapshot.Timeline[3].(*AddCommentTimelineItem).History), 1)
+ require.Equal(t, snapshot.Comments[0].Message, "create edited")
+ require.Equal(t, snapshot.Comments[1].Message, "comment 1")
+ require.Equal(t, snapshot.Comments[2].Message, "comment 2")
edit2 := NewEditCommentOp(rene, unix, id2, "comment 1 edited", nil)
edit2.Apply(&snapshot)
- assert.Equal(t, len(snapshot.Timeline), 4)
- assert.Equal(t, len(snapshot.Timeline[0].(*CreateTimelineItem).History), 2)
- assert.Equal(t, len(snapshot.Timeline[1].(*AddCommentTimelineItem).History), 2)
- assert.Equal(t, len(snapshot.Timeline[3].(*AddCommentTimelineItem).History), 1)
- assert.Equal(t, snapshot.Comments[0].Message, "create edited")
- assert.Equal(t, snapshot.Comments[1].Message, "comment 1 edited")
- assert.Equal(t, snapshot.Comments[2].Message, "comment 2")
+ require.Equal(t, len(snapshot.Timeline), 4)
+ require.Equal(t, len(snapshot.Timeline[0].(*CreateTimelineItem).History), 2)
+ require.Equal(t, len(snapshot.Timeline[1].(*AddCommentTimelineItem).History), 2)
+ require.Equal(t, len(snapshot.Timeline[3].(*AddCommentTimelineItem).History), 1)
+ require.Equal(t, snapshot.Comments[0].Message, "create edited")
+ require.Equal(t, snapshot.Comments[1].Message, "comment 1 edited")
+ require.Equal(t, snapshot.Comments[2].Message, "comment 2")
edit3 := NewEditCommentOp(rene, unix, id3, "comment 2 edited", nil)
edit3.Apply(&snapshot)
- assert.Equal(t, len(snapshot.Timeline), 4)
- assert.Equal(t, len(snapshot.Timeline[0].(*CreateTimelineItem).History), 2)
- assert.Equal(t, len(snapshot.Timeline[1].(*AddCommentTimelineItem).History), 2)
- assert.Equal(t, len(snapshot.Timeline[3].(*AddCommentTimelineItem).History), 2)
- assert.Equal(t, snapshot.Comments[0].Message, "create edited")
- assert.Equal(t, snapshot.Comments[1].Message, "comment 1 edited")
- assert.Equal(t, snapshot.Comments[2].Message, "comment 2 edited")
+ require.Equal(t, len(snapshot.Timeline), 4)
+ require.Equal(t, len(snapshot.Timeline[0].(*CreateTimelineItem).History), 2)
+ require.Equal(t, len(snapshot.Timeline[1].(*AddCommentTimelineItem).History), 2)
+ require.Equal(t, len(snapshot.Timeline[3].(*AddCommentTimelineItem).History), 2)
+ require.Equal(t, snapshot.Comments[0].Message, "create edited")
+ require.Equal(t, snapshot.Comments[1].Message, "comment 1 edited")
+ require.Equal(t, snapshot.Comments[2].Message, "comment 2 edited")
}
func TestEditCommentSerialize(t *testing.T) {
repo := repository.NewMockRepo()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(repo)
+
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
require.NoError(t, err)
unix := time.Now().Unix()
before := NewEditCommentOp(rene, unix, "target", "message", nil)
data, err := json.Marshal(before)
- assert.NoError(t, err)
+ require.NoError(t, err)
var after EditCommentOperation
err = json.Unmarshal(data, &after)
- assert.NoError(t, err)
+ require.NoError(t, err)
// enforce creating the ID
before.Id()
// Replace the identity stub with the real thing
- assert.Equal(t, rene.Id(), after.base().Author.Id())
+ require.Equal(t, rene.Id(), after.base().Author.Id())
after.Author = rene
- assert.Equal(t, before, &after)
+ require.Equal(t, before, &after)
}
diff --git a/bug/op_label_change_test.go b/bug/op_label_change_test.go
index ea73368c..96716ffe 100644
--- a/bug/op_label_change_test.go
+++ b/bug/op_label_change_test.go
@@ -9,32 +9,30 @@ import (
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
-
- "github.com/stretchr/testify/assert"
)
func TestLabelChangeSerialize(t *testing.T) {
repo := repository.NewMockRepo()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(repo)
+
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
require.NoError(t, err)
unix := time.Now().Unix()
before := NewLabelChangeOperation(rene, unix, []Label{"added"}, []Label{"removed"})
data, err := json.Marshal(before)
- assert.NoError(t, err)
+ require.NoError(t, err)
var after LabelChangeOperation
err = json.Unmarshal(data, &after)
- assert.NoError(t, err)
+ require.NoError(t, err)
// enforce creating the ID
before.Id()
// Replace the identity stub with the real thing
- assert.Equal(t, rene.Id(), after.base().Author.Id())
+ require.Equal(t, rene.Id(), after.base().Author.Id())
after.Author = rene
- assert.Equal(t, before, &after)
+ require.Equal(t, before, &after)
}
diff --git a/bug/op_noop_test.go b/bug/op_noop_test.go
index 812851ea..ce2f98af 100644
--- a/bug/op_noop_test.go
+++ b/bug/op_noop_test.go
@@ -15,8 +15,8 @@ import (
func TestNoopSerialize(t *testing.T) {
repo := repository.NewMockRepo()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(repo)
+
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
require.NoError(t, err)
unix := time.Now().Unix()
diff --git a/bug/op_set_metadata_test.go b/bug/op_set_metadata_test.go
index ba068f61..c90f192a 100644
--- a/bug/op_set_metadata_test.go
+++ b/bug/op_set_metadata_test.go
@@ -8,7 +8,6 @@ import (
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -16,8 +15,8 @@ func TestSetMetadata(t *testing.T) {
snapshot := Snapshot{}
repo := repository.NewMockRepo()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(repo)
+
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
require.NoError(t, err)
unix := time.Now().Unix()
@@ -47,15 +46,15 @@ func TestSetMetadata(t *testing.T) {
snapshot.Operations = append(snapshot.Operations, op1)
createMetadata := snapshot.Operations[0].AllMetadata()
- assert.Equal(t, len(createMetadata), 2)
+ require.Equal(t, len(createMetadata), 2)
// original key is not overrided
- assert.Equal(t, createMetadata["key"], "value")
+ require.Equal(t, createMetadata["key"], "value")
// new key is set
- assert.Equal(t, createMetadata["key2"], "value")
+ require.Equal(t, createMetadata["key2"], "value")
commentMetadata := snapshot.Operations[1].AllMetadata()
- assert.Equal(t, len(commentMetadata), 1)
- assert.Equal(t, commentMetadata["key2"], "value2")
+ require.Equal(t, len(commentMetadata), 1)
+ require.Equal(t, commentMetadata["key2"], "value2")
op2 := NewSetMetadataOp(rene, unix, id2, map[string]string{
"key2": "value",
@@ -66,16 +65,16 @@ func TestSetMetadata(t *testing.T) {
snapshot.Operations = append(snapshot.Operations, op2)
createMetadata = snapshot.Operations[0].AllMetadata()
- assert.Equal(t, len(createMetadata), 2)
- assert.Equal(t, createMetadata["key"], "value")
- assert.Equal(t, createMetadata["key2"], "value")
+ require.Equal(t, len(createMetadata), 2)
+ require.Equal(t, createMetadata["key"], "value")
+ require.Equal(t, createMetadata["key2"], "value")
commentMetadata = snapshot.Operations[1].AllMetadata()
- assert.Equal(t, len(commentMetadata), 2)
+ require.Equal(t, len(commentMetadata), 2)
// original key is not overrided
- assert.Equal(t, commentMetadata["key2"], "value2")
+ require.Equal(t, commentMetadata["key2"], "value2")
// new key is set
- assert.Equal(t, commentMetadata["key3"], "value3")
+ require.Equal(t, commentMetadata["key3"], "value3")
op3 := NewSetMetadataOp(rene, unix, id1, map[string]string{
"key": "override",
@@ -86,22 +85,22 @@ func TestSetMetadata(t *testing.T) {
snapshot.Operations = append(snapshot.Operations, op3)
createMetadata = snapshot.Operations[0].AllMetadata()
- assert.Equal(t, len(createMetadata), 2)
+ require.Equal(t, len(createMetadata), 2)
// original key is not overrided
- assert.Equal(t, createMetadata["key"], "value")
+ require.Equal(t, createMetadata["key"], "value")
// previously set key is not overrided
- assert.Equal(t, createMetadata["key2"], "value")
+ require.Equal(t, createMetadata["key2"], "value")
commentMetadata = snapshot.Operations[1].AllMetadata()
- assert.Equal(t, len(commentMetadata), 2)
- assert.Equal(t, commentMetadata["key2"], "value2")
- assert.Equal(t, commentMetadata["key3"], "value3")
+ require.Equal(t, len(commentMetadata), 2)
+ require.Equal(t, commentMetadata["key2"], "value2")
+ require.Equal(t, commentMetadata["key3"], "value3")
}
func TestSetMetadataSerialize(t *testing.T) {
repo := repository.NewMockRepo()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(repo)
+
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
require.NoError(t, err)
unix := time.Now().Unix()
@@ -111,18 +110,18 @@ func TestSetMetadataSerialize(t *testing.T) {
})
data, err := json.Marshal(before)
- assert.NoError(t, err)
+ require.NoError(t, err)
var after SetMetadataOperation
err = json.Unmarshal(data, &after)
- assert.NoError(t, err)
+ require.NoError(t, err)
// enforce creating the ID
before.Id()
// Replace the identity stub with the real thing
- assert.Equal(t, rene.Id(), after.base().Author.Id())
+ require.Equal(t, rene.Id(), after.base().Author.Id())
after.Author = rene
- assert.Equal(t, before, &after)
+ require.Equal(t, before, &after)
}
diff --git a/bug/op_set_status_test.go b/bug/op_set_status_test.go
index 0619c913..3b26282f 100644
--- a/bug/op_set_status_test.go
+++ b/bug/op_set_status_test.go
@@ -9,32 +9,30 @@ import (
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
-
- "github.com/stretchr/testify/assert"
)
func TestSetStatusSerialize(t *testing.T) {
repo := repository.NewMockRepo()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(repo)
+
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
require.NoError(t, err)
unix := time.Now().Unix()
before := NewSetStatusOp(rene, unix, ClosedStatus)
data, err := json.Marshal(before)
- assert.NoError(t, err)
+ require.NoError(t, err)
var after SetStatusOperation
err = json.Unmarshal(data, &after)
- assert.NoError(t, err)
+ require.NoError(t, err)
// enforce creating the ID
before.Id()
// Replace the identity stub with the real thing
- assert.Equal(t, rene.Id(), after.base().Author.Id())
+ require.Equal(t, rene.Id(), after.base().Author.Id())
after.Author = rene
- assert.Equal(t, before, &after)
+ require.Equal(t, before, &after)
}
diff --git a/bug/op_set_title_test.go b/bug/op_set_title_test.go
index df27ee35..6ae325be 100644
--- a/bug/op_set_title_test.go
+++ b/bug/op_set_title_test.go
@@ -9,32 +9,30 @@ import (
"github.com/MichaelMure/git-bug/identity"
"github.com/MichaelMure/git-bug/repository"
-
- "github.com/stretchr/testify/assert"
)
func TestSetTitleSerialize(t *testing.T) {
repo := repository.NewMockRepo()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(repo)
+
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
require.NoError(t, err)
unix := time.Now().Unix()
before := NewSetTitleOp(rene, unix, "title", "was")
data, err := json.Marshal(before)
- assert.NoError(t, err)
+ require.NoError(t, err)
var after SetTitleOperation
err = json.Unmarshal(data, &after)
- assert.NoError(t, err)
+ require.NoError(t, err)
// enforce creating the ID
before.Id()
// Replace the identity stub with the real thing
- assert.Equal(t, rene.Id(), after.base().Author.Id())
+ require.Equal(t, rene.Id(), after.base().Author.Id())
after.Author = rene
- assert.Equal(t, before, &after)
+ require.Equal(t, before, &after)
}
diff --git a/bug/operation_iterator_test.go b/bug/operation_iterator_test.go
index e066ddd8..81d87a5f 100644
--- a/bug/operation_iterator_test.go
+++ b/bug/operation_iterator_test.go
@@ -25,10 +25,11 @@ func ExampleOperationIterator() {
}
func TestOpIterator(t *testing.T) {
- mockRepo := repository.NewMockRepo()
+ repo := repository.NewMockRepo()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(mockRepo)
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+ err = rene.Commit(repo)
require.NoError(t, err)
unix := time.Now().Unix()
@@ -51,14 +52,14 @@ func TestOpIterator(t *testing.T) {
bug1.Append(addCommentOp)
bug1.Append(setStatusOp)
bug1.Append(labelChangeOp)
- err = bug1.Commit(mockRepo)
+ err = bug1.Commit(repo)
require.NoError(t, err)
// second pack
bug1.Append(genTitleOp())
bug1.Append(genTitleOp())
bug1.Append(genTitleOp())
- err = bug1.Commit(mockRepo)
+ err = bug1.Commit(repo)
require.NoError(t, err)
// staging
diff --git a/bug/operation_pack.go b/bug/operation_pack.go
index 1a8ef0db..74d15f50 100644
--- a/bug/operation_pack.go
+++ b/bug/operation_pack.go
@@ -12,7 +12,8 @@ import (
// 1: original format
// 2: no more legacy identities
-const formatVersion = 2
+// 3: Ids are generated from the create operation serialized data instead of from the first git commit
+const formatVersion = 3
// OperationPack represent an ordered set of operation to apply
// to a Bug. These operations are stored in a single Git commit.
@@ -158,13 +159,11 @@ func (opp *OperationPack) Write(repo repository.ClockedRepo) (repository.Hash, e
}
data, err := json.Marshal(opp)
-
if err != nil {
return "", err
}
hash, err := repo.StoreData(data)
-
if err != nil {
return "", err
}
diff --git a/bug/operation_pack_test.go b/bug/operation_pack_test.go
index e1388240..02d72f0f 100644
--- a/bug/operation_pack_test.go
+++ b/bug/operation_pack_test.go
@@ -5,7 +5,6 @@ import (
"testing"
"time"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/MichaelMure/git-bug/identity"
@@ -16,8 +15,8 @@ func TestOperationPackSerialize(t *testing.T) {
opp := &OperationPack{}
repo := repository.NewMockRepo()
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(repo)
+
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
require.NoError(t, err)
createOp := NewCreateOp(rene, time.Now().Unix(), "title", "message", nil)
@@ -36,7 +35,7 @@ func TestOperationPackSerialize(t *testing.T) {
opMeta.SetMetadata("key", "value")
opp.Append(opMeta)
- assert.Equal(t, 1, len(opMeta.Metadata))
+ require.Equal(t, 1, len(opMeta.Metadata))
opFile := NewAddCommentOp(rene, time.Now().Unix(), "message", []repository.Hash{
"abcdef",
@@ -44,19 +43,19 @@ func TestOperationPackSerialize(t *testing.T) {
})
opp.Append(opFile)
- assert.Equal(t, 2, len(opFile.Files))
+ require.Equal(t, 2, len(opFile.Files))
data, err := json.Marshal(opp)
- assert.NoError(t, err)
+ require.NoError(t, err)
var opp2 *OperationPack
err = json.Unmarshal(data, &opp2)
- assert.NoError(t, err)
+ require.NoError(t, err)
ensureIds(opp)
ensureAuthors(t, opp, opp2)
- assert.Equal(t, opp, opp2)
+ require.Equal(t, opp, opp2)
}
func ensureIds(opp *OperationPack) {
diff --git a/bug/operation_test.go b/bug/operation_test.go
index 91e1d936..f66938ad 100644
--- a/bug/operation_test.go
+++ b/bug/operation_test.go
@@ -11,7 +11,16 @@ import (
)
func TestValidate(t *testing.T) {
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
+ repo := repository.NewMockRepoClock()
+
+ makeIdentity := func(t *testing.T, name, email string) *identity.Identity {
+ i, err := identity.NewIdentity(repo, name, email)
+ require.NoError(t, err)
+ return i
+ }
+
+ rene := makeIdentity(t, "René Descartes", "rene@descartes.fr")
+
unix := time.Now().Unix()
good := []Operation{
@@ -30,11 +39,11 @@ func TestValidate(t *testing.T) {
bad := []Operation{
// opbase
- NewSetStatusOp(identity.NewIdentity("", "rene@descartes.fr"), unix, ClosedStatus),
- NewSetStatusOp(identity.NewIdentity("René Descartes\u001b", "rene@descartes.fr"), unix, ClosedStatus),
- NewSetStatusOp(identity.NewIdentity("René Descartes", "rene@descartes.fr\u001b"), unix, ClosedStatus),
- NewSetStatusOp(identity.NewIdentity("René \nDescartes", "rene@descartes.fr"), unix, ClosedStatus),
- NewSetStatusOp(identity.NewIdentity("René Descartes", "rene@\ndescartes.fr"), unix, ClosedStatus),
+ NewSetStatusOp(makeIdentity(t, "", "rene@descartes.fr"), unix, ClosedStatus),
+ NewSetStatusOp(makeIdentity(t, "René Descartes\u001b", "rene@descartes.fr"), unix, ClosedStatus),
+ NewSetStatusOp(makeIdentity(t, "René Descartes", "rene@descartes.fr\u001b"), unix, ClosedStatus),
+ NewSetStatusOp(makeIdentity(t, "René \nDescartes", "rene@descartes.fr"), unix, ClosedStatus),
+ NewSetStatusOp(makeIdentity(t, "René Descartes", "rene@\ndescartes.fr"), unix, ClosedStatus),
&CreateOperation{OpBase: OpBase{
Author: rene,
UnixTime: 0,
@@ -68,7 +77,11 @@ func TestValidate(t *testing.T) {
}
func TestMetadata(t *testing.T) {
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
+ repo := repository.NewMockRepoClock()
+
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+
op := NewCreateOp(rene, time.Now().Unix(), "title", "message", nil)
op.SetMetadata("key", "value")
@@ -88,8 +101,9 @@ func TestID(t *testing.T) {
}
for _, repo := range repos {
- rene := identity.NewIdentity("René Descartes", "rene@descartes.fr")
- err := rene.Commit(repo)
+ rene, err := identity.NewIdentity(repo, "René Descartes", "rene@descartes.fr")
+ require.NoError(t, err)
+ err = rene.Commit(repo)
require.NoError(t, err)
b, op, err := Create(rene, time.Now().Unix(), "title", "message")
diff --git a/entity/refs.go b/entity/refs.go
index 82b1741b..f505dbf0 100644
--- a/entity/refs.go
+++ b/entity/refs.go
@@ -6,13 +6,13 @@ func RefsToIds(refs []string) []Id {
ids := make([]Id, len(refs))
for i, ref := range refs {
- ids[i] = refToId(ref)
+ ids[i] = RefToId(ref)
}
return ids
}
-func refToId(ref string) Id {
+func RefToId(ref string) Id {
split := strings.Split(ref, "/")
return Id(split[len(split)-1])
}
diff --git a/identity/identity.go b/identity/identity.go
index 6352212d..ef488712 100644
--- a/identity/identity.go
+++ b/identity/identity.go
@@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"reflect"
- "strings"
"github.com/pkg/errors"
@@ -102,8 +101,7 @@ func ReadRemote(repo repository.Repo, remote string, id string) (*Identity, erro
// read will load and parse an identity from git
func read(repo repository.Repo, ref string) (*Identity, error) {
- refSplit := strings.Split(ref, "/")
- id := entity.Id(refSplit[len(refSplit)-1])
+ id := entity.RefToId(ref)
if err := id.Validate(); err != nil {
return nil, errors.Wrap(err, "invalid ref")