aboutsummaryrefslogtreecommitdiffstats
path: root/entity/dag
diff options
context:
space:
mode:
authorMichael Muré <batolettre@gmail.com>2021-01-25 12:39:34 +0100
committerMichael Muré <batolettre@gmail.com>2021-02-14 12:19:01 +0100
commitfe4237df3c62bd6dfd1f385893295f93072d0e51 (patch)
tree66eabac7a2aaae9f7b4f2aff07092556e3a2a2ac /entity/dag
parentdc5059bc3372941e2908739831188768335ac50b (diff)
downloadgit-bug-fe4237df3c62bd6dfd1f385893295f93072d0e51.tar.gz
entity: readAll and more testing
Diffstat (limited to 'entity/dag')
-rw-r--r--entity/dag/common_test.go38
-rw-r--r--entity/dag/entity.go48
-rw-r--r--entity/dag/entity_actions.go8
-rw-r--r--entity/dag/entity_actions_test.go110
4 files changed, 190 insertions, 14 deletions
diff --git a/entity/dag/common_test.go b/entity/dag/common_test.go
index 29f1279e..b822fc79 100644
--- a/entity/dag/common_test.go
+++ b/entity/dag/common_test.go
@@ -26,16 +26,16 @@ func newOp1(author identity.Interface, field1 string) *op1 {
return &op1{author: author, OperationType: 1, Field1: field1}
}
-func (o op1) Id() entity.Id {
+func (o *op1) Id() entity.Id {
data, _ := json.Marshal(o)
return entity.DeriveId(data)
}
-func (o op1) Author() identity.Interface {
+func (o *op1) Author() identity.Interface {
return o.author
}
-func (o op1) Validate() error { return nil }
+func (o *op1) Validate() error { return nil }
type op2 struct {
author identity.Interface
@@ -48,16 +48,16 @@ func newOp2(author identity.Interface, field2 string) *op2 {
return &op2{author: author, OperationType: 2, Field2: field2}
}
-func (o op2) Id() entity.Id {
+func (o *op2) Id() entity.Id {
data, _ := json.Marshal(o)
return entity.DeriveId(data)
}
-func (o op2) Author() identity.Interface {
+func (o *op2) Author() identity.Interface {
return o.author
}
-func (o op2) Validate() error { return nil }
+func (o *op2) Validate() error { return nil }
func unmarshaler(author identity.Interface, raw json.RawMessage) (Operation, error) {
var t struct {
@@ -90,7 +90,31 @@ func unmarshaler(author identity.Interface, raw json.RawMessage) (Operation, err
func makeTestContext() (repository.ClockedRepo, identity.Interface, identity.Interface, Definition) {
repo := repository.NewMockRepo()
+ id1, id2, def := makeTestContextInternal(repo)
+ return repo, id1, id2, def
+}
+
+func makeTestContextRemote() (repository.ClockedRepo, repository.ClockedRepo, repository.ClockedRepo, identity.Interface, identity.Interface, Definition) {
+ repoA := repository.CreateGoGitTestRepo(false)
+ repoB := repository.CreateGoGitTestRepo(false)
+ remote := repository.CreateGoGitTestRepo(true)
+
+ err := repoA.AddRemote("origin", remote.GetLocalRemote())
+ if err != nil {
+ panic(err)
+ }
+ err = repoB.AddRemote("origin", remote.GetLocalRemote())
+ if err != nil {
+ panic(err)
+ }
+
+ id1, id2, def := makeTestContextInternal(repoA)
+
+ return repoA, repoB, remote, id1, id2, def
+}
+
+func makeTestContextInternal(repo repository.ClockedRepo) (identity.Interface, identity.Interface, Definition) {
id1, err := identity.NewIdentity(repo, "name1", "email1")
if err != nil {
panic(err)
@@ -127,7 +151,7 @@ func makeTestContext() (repository.ClockedRepo, identity.Interface, identity.Int
formatVersion: 1,
}
- return repo, id1, id2, def
+ return id1, id2, def
}
type identityResolverFunc func(id entity.Id) (identity.Interface, error)
diff --git a/entity/dag/entity.go b/entity/dag/entity.go
index 63d7fc3b..d3f5b482 100644
--- a/entity/dag/entity.go
+++ b/entity/dag/entity.go
@@ -58,7 +58,7 @@ func New(definition Definition) *Entity {
}
}
-// Read will read and decode a stored Entity from a repository
+// Read will read and decode a stored local Entity from a repository
func Read(def Definition, repo repository.ClockedRepo, id entity.Id) (*Entity, error) {
if err := id.Validate(); err != nil {
return nil, errors.Wrap(err, "invalid id")
@@ -69,6 +69,17 @@ func Read(def Definition, repo repository.ClockedRepo, id entity.Id) (*Entity, e
return read(def, repo, ref)
}
+// readRemote will read and decode a stored remote Entity from a repository
+func readRemote(def Definition, repo repository.ClockedRepo, remote string, id entity.Id) (*Entity, error) {
+ if err := id.Validate(); err != nil {
+ return nil, errors.Wrap(err, "invalid id")
+ }
+
+ ref := fmt.Sprintf("refs/remotes/%s/%s/%s", def.namespace, remote, id.String())
+
+ return read(def, repo, ref)
+}
+
// read fetch from git and decode an Entity at an arbitrary git reference.
func read(def Definition, repo repository.ClockedRepo, ref string) (*Entity, error) {
rootHash, err := repo.ResolveRef(ref)
@@ -232,6 +243,41 @@ func read(def Definition, repo repository.ClockedRepo, ref string) (*Entity, err
}, nil
}
+type StreamedEntity struct {
+ Entity *Entity
+ Err error
+}
+
+// ReadAll read and parse all local Entity
+func ReadAll(def Definition, repo repository.ClockedRepo) <-chan StreamedEntity {
+ out := make(chan StreamedEntity)
+
+ go func() {
+ defer close(out)
+
+ refPrefix := fmt.Sprintf("refs/%s/", def.namespace)
+
+ refs, err := repo.ListRefs(refPrefix)
+ if err != nil {
+ out <- StreamedEntity{Err: err}
+ return
+ }
+
+ for _, ref := range refs {
+ e, err := read(def, repo, ref)
+
+ if err != nil {
+ out <- StreamedEntity{Err: err}
+ return
+ }
+
+ out <- StreamedEntity{Entity: e}
+ }
+ }()
+
+ return out
+}
+
// Id return the Entity identifier
func (e *Entity) Id() entity.Id {
// id is the id of the first operation
diff --git a/entity/dag/entity_actions.go b/entity/dag/entity_actions.go
index 83ff7ddc..db3a545c 100644
--- a/entity/dag/entity_actions.go
+++ b/entity/dag/entity_actions.go
@@ -10,8 +10,8 @@ import (
)
// ListLocalIds list all the available local Entity's Id
-func ListLocalIds(typename string, repo repository.RepoData) ([]entity.Id, error) {
- refs, err := repo.ListRefs(fmt.Sprintf("refs/%s/", typename))
+func ListLocalIds(def Definition, repo repository.RepoData) ([]entity.Id, error) {
+ refs, err := repo.ListRefs(fmt.Sprintf("refs/%s/", def.namespace))
if err != nil {
return nil, err
}
@@ -75,10 +75,6 @@ func Pull(def Definition, repo repository.ClockedRepo, remote string) error {
func MergeAll(def Definition, repo repository.ClockedRepo, remote string) <-chan entity.MergeResult {
out := make(chan entity.MergeResult)
- // no caching for the merge, we load everything from git even if that means multiple
- // copy of the same entity in memory. The cache layer will intercept the results to
- // invalidate entities if necessary.
-
go func() {
defer close(out)
diff --git a/entity/dag/entity_actions_test.go b/entity/dag/entity_actions_test.go
new file mode 100644
index 00000000..6cc544b6
--- /dev/null
+++ b/entity/dag/entity_actions_test.go
@@ -0,0 +1,110 @@
+package dag
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/MichaelMure/git-bug/identity"
+ "github.com/MichaelMure/git-bug/repository"
+)
+
+func allEntities(t testing.TB, bugs <-chan StreamedEntity) []*Entity {
+ var result []*Entity
+ for streamed := range bugs {
+ if streamed.Err != nil {
+ t.Fatal(streamed.Err)
+ }
+ result = append(result, streamed.Entity)
+ }
+ return result
+}
+
+func TestPushPull(t *testing.T) {
+ repoA, repoB, remote, id1, id2, def := makeTestContextRemote()
+ defer repository.CleanupTestRepos(repoA, repoB, remote)
+
+ // distribute the identities
+ _, err := identity.Push(repoA, "origin")
+ require.NoError(t, err)
+ err = identity.Pull(repoB, "origin")
+ require.NoError(t, err)
+
+ // A --> remote --> B
+ entity := New(def)
+ entity.Append(newOp1(id1, "foo"))
+
+ err = entity.Commit(repoA)
+ require.NoError(t, err)
+
+ _, err = Push(def, repoA, "origin")
+ require.NoError(t, err)
+
+ err = Pull(def, repoB, "origin")
+ require.NoError(t, err)
+
+ entities := allEntities(t, ReadAll(def, repoB))
+ require.Len(t, entities, 1)
+
+ // B --> remote --> A
+ entity = New(def)
+ entity.Append(newOp2(id2, "bar"))
+
+ err = entity.Commit(repoB)
+ require.NoError(t, err)
+
+ _, err = Push(def, repoB, "origin")
+ require.NoError(t, err)
+
+ err = Pull(def, repoA, "origin")
+ require.NoError(t, err)
+
+ entities = allEntities(t, ReadAll(def, repoB))
+ require.Len(t, entities, 2)
+}
+
+func TestListLocalIds(t *testing.T) {
+ repoA, repoB, remote, id1, id2, def := makeTestContextRemote()
+ defer repository.CleanupTestRepos(repoA, repoB, remote)
+
+ // distribute the identities
+ _, err := identity.Push(repoA, "origin")
+ require.NoError(t, err)
+ err = identity.Pull(repoB, "origin")
+ require.NoError(t, err)
+
+ // A --> remote --> B
+ entity := New(def)
+ entity.Append(newOp1(id1, "foo"))
+ err = entity.Commit(repoA)
+ require.NoError(t, err)
+
+ entity = New(def)
+ entity.Append(newOp2(id2, "bar"))
+ err = entity.Commit(repoA)
+ require.NoError(t, err)
+
+ listLocalIds(t, def, repoA, 2)
+ listLocalIds(t, def, repoB, 0)
+
+ _, err = Push(def, repoA, "origin")
+ require.NoError(t, err)
+
+ _, err = Fetch(def, repoB, "origin")
+ require.NoError(t, err)
+
+ listLocalIds(t, def, repoA, 2)
+ listLocalIds(t, def, repoB, 0)
+
+ err = Pull(def, repoB, "origin")
+ require.NoError(t, err)
+
+ listLocalIds(t, def, repoA, 2)
+ listLocalIds(t, def, repoB, 2)
+}
+
+func listLocalIds(t *testing.T, def Definition, repo repository.RepoData, expectedCount int) {
+ ids, err := ListLocalIds(def, repo)
+ require.NoError(t, err)
+ require.Len(t, ids, expectedCount)
+}