diff options
-rw-r--r-- | bug/bug_actions.go | 3 | ||||
-rw-r--r-- | bug/bug_actions_test.go | 8 | ||||
-rw-r--r-- | cache/repo_cache_test.go | 2 | ||||
-rw-r--r-- | entity/dag/common_test.go | 38 | ||||
-rw-r--r-- | entity/dag/entity.go | 48 | ||||
-rw-r--r-- | entity/dag/entity_actions.go | 8 | ||||
-rw-r--r-- | entity/dag/entity_actions_test.go | 110 | ||||
-rw-r--r-- | repository/mock_repo.go | 4 | ||||
-rw-r--r-- | repository/repo.go | 1 | ||||
-rw-r--r-- | repository/repo_testing.go | 3 |
10 files changed, 203 insertions, 22 deletions
diff --git a/bug/bug_actions.go b/bug/bug_actions.go index 40a2facb..0e240711 100644 --- a/bug/bug_actions.go +++ b/bug/bug_actions.go @@ -4,10 +4,11 @@ import ( "fmt" "strings" + "github.com/pkg/errors" + "github.com/MichaelMure/git-bug/entity" "github.com/MichaelMure/git-bug/identity" "github.com/MichaelMure/git-bug/repository" - "github.com/pkg/errors" ) // Fetch retrieve updates from a remote diff --git a/bug/bug_actions_test.go b/bug/bug_actions_test.go index 7a9673d6..fc671063 100644 --- a/bug/bug_actions_test.go +++ b/bug/bug_actions_test.go @@ -12,7 +12,7 @@ import ( ) func TestPushPull(t *testing.T) { - repoA, repoB, remote := repository.SetupReposAndRemote() + repoA, repoB, remote := repository.SetupGoGitReposAndRemote() defer repository.CleanupTestRepos(repoA, repoB, remote) reneA, err := identity.NewIdentity(repoA, "René Descartes", "rene@descartes.fr") @@ -90,7 +90,7 @@ func BenchmarkRebaseTheirs(b *testing.B) { } func _RebaseTheirs(t testing.TB) { - repoA, repoB, remote := repository.SetupReposAndRemote() + repoA, repoB, remote := repository.SetupGoGitReposAndRemote() defer repository.CleanupTestRepos(repoA, repoB, remote) reneA, err := identity.NewIdentity(repoA, "René Descartes", "rene@descartes.fr") @@ -171,7 +171,7 @@ func BenchmarkRebaseOurs(b *testing.B) { } func _RebaseOurs(t testing.TB) { - repoA, repoB, remote := repository.SetupReposAndRemote() + repoA, repoB, remote := repository.SetupGoGitReposAndRemote() defer repository.CleanupTestRepos(repoA, repoB, remote) reneA, err := identity.NewIdentity(repoA, "René Descartes", "rene@descartes.fr") @@ -263,7 +263,7 @@ func BenchmarkRebaseConflict(b *testing.B) { } func _RebaseConflict(t testing.TB) { - repoA, repoB, remote := repository.SetupReposAndRemote() + repoA, repoB, remote := repository.SetupGoGitReposAndRemote() defer repository.CleanupTestRepos(repoA, repoB, remote) reneA, err := identity.NewIdentity(repoA, "René Descartes", "rene@descartes.fr") diff --git a/cache/repo_cache_test.go b/cache/repo_cache_test.go index bd06e84d..9cdd584d 100644 --- a/cache/repo_cache_test.go +++ b/cache/repo_cache_test.go @@ -109,7 +109,7 @@ func TestCache(t *testing.T) { } func TestPushPull(t *testing.T) { - repoA, repoB, remote := repository.SetupReposAndRemote() + repoA, repoB, remote := repository.SetupGoGitReposAndRemote() defer repository.CleanupTestRepos(repoA, repoB, remote) cacheA, err := NewRepoCache(repoA) 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) +} diff --git a/repository/mock_repo.go b/repository/mock_repo.go index 095ad61c..adbceec2 100644 --- a/repository/mock_repo.go +++ b/repository/mock_repo.go @@ -202,12 +202,12 @@ func NewMockRepoData() *mockRepoData { } func (r *mockRepoData) FetchRefs(remote string, refSpec string) (string, error) { - return "", nil + panic("implement me") } // PushRefs push git refs to a remote func (r *mockRepoData) PushRefs(remote string, refSpec string) (string, error) { - return "", nil + panic("implement me") } func (r *mockRepoData) StoreData(data []byte) (Hash, error) { diff --git a/repository/repo.go b/repository/repo.go index 87426333..c31afa22 100644 --- a/repository/repo.go +++ b/repository/repo.go @@ -130,6 +130,7 @@ type RepoData interface { ReadCommit(hash Hash) (Commit, error) // GetTreeHash return the git tree hash referenced in a commit + // Deprecated GetTreeHash(commit Hash) (Hash, error) // ResolveRef returns the hash of the target commit of the given ref diff --git a/repository/repo_testing.go b/repository/repo_testing.go index 92aa1691..8fc109bb 100644 --- a/repository/repo_testing.go +++ b/repository/repo_testing.go @@ -10,6 +10,9 @@ import ( "github.com/MichaelMure/git-bug/util/lamport" ) +// TODO: add tests for RepoBleve +// TODO: add tests for RepoStorage + func CleanupTestRepos(repos ...Repo) { var firstErr error for _, repo := range repos { |