Adding upstream version 3.10.8.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
37e9b6d587
commit
03bfe4079e
356 changed files with 28857 additions and 0 deletions
397
tree/tests/f3/creator.go
Normal file
397
tree/tests/f3/creator.go
Normal file
|
@ -0,0 +1,397 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package f3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"code.forgejo.org/f3/gof3/v3/f3"
|
||||
helpers_repository "code.forgejo.org/f3/gof3/v3/forges/helpers/repository"
|
||||
tests_repository "code.forgejo.org/f3/gof3/v3/forges/helpers/tests/repository"
|
||||
"code.forgejo.org/f3/gof3/v3/kind"
|
||||
"code.forgejo.org/f3/gof3/v3/logger"
|
||||
"code.forgejo.org/f3/gof3/v3/path"
|
||||
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var RootUser *f3.User = &f3.User{
|
||||
UserName: "root",
|
||||
}
|
||||
|
||||
type Creator struct {
|
||||
logger logger.Interface
|
||||
t TestingT
|
||||
d string
|
||||
name string
|
||||
serial int
|
||||
}
|
||||
|
||||
func now() time.Time {
|
||||
return time.Now().Truncate(time.Second)
|
||||
}
|
||||
|
||||
func tick(now *time.Time) time.Time {
|
||||
*now = now.Add(1 * time.Minute)
|
||||
return *now
|
||||
}
|
||||
|
||||
func NewCreator(t TestingT, name string, logger logger.Interface) *Creator {
|
||||
return &Creator{
|
||||
t: t,
|
||||
d: t.TempDir(),
|
||||
logger: logger,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) randomString(prefix string) string {
|
||||
f.serial++
|
||||
return fmt.Sprintf("%s%s%015d", prefix, f.name, f.serial)
|
||||
}
|
||||
|
||||
func (f *Creator) GetDirectory() string {
|
||||
return f.d
|
||||
}
|
||||
|
||||
func (f *Creator) Generate(k kind.Kind, parent path.Path) f3.Interface {
|
||||
switch k {
|
||||
case f3_tree.KindForge:
|
||||
return f.GenerateForge()
|
||||
case f3_tree.KindUser:
|
||||
return f.GenerateUser()
|
||||
case f3_tree.KindOrganization:
|
||||
return f.GenerateOrganization()
|
||||
case f3_tree.KindProject:
|
||||
return f.GenerateProject()
|
||||
case f3_tree.KindIssue:
|
||||
return f.GenerateIssue(parent)
|
||||
case f3_tree.KindMilestone:
|
||||
return f.GenerateMilestone()
|
||||
case f3_tree.KindTopic:
|
||||
return f.GenerateTopic()
|
||||
case f3_tree.KindReaction:
|
||||
return f.GenerateReaction(parent)
|
||||
case f3_tree.KindLabel:
|
||||
return f.GenerateLabel()
|
||||
case f3_tree.KindComment:
|
||||
return f.GenerateComment(parent)
|
||||
case f3_tree.KindRepository:
|
||||
return f.GenerateRepository(f3.RepositoryNameDefault)
|
||||
case f3_tree.KindRelease:
|
||||
return f.GenerateRelease(parent)
|
||||
case f3_tree.KindAsset:
|
||||
return f.GenerateAsset(parent)
|
||||
case f3_tree.KindPullRequest:
|
||||
return f.GeneratePullRequest(parent)
|
||||
case f3_tree.KindReview:
|
||||
return f.GenerateReview(parent)
|
||||
case f3_tree.KindReviewComment:
|
||||
return f.GenerateReviewComment(parent)
|
||||
default:
|
||||
panic(fmt.Errorf("not implemented %s", k))
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateForge() *f3.Forge {
|
||||
return &f3.Forge{
|
||||
Common: f3.NewCommon("forge"),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateUser() *f3.User {
|
||||
username := f.randomString("user")
|
||||
return &f3.User{
|
||||
Name: username + " Doe",
|
||||
UserName: username,
|
||||
Email: username + "@example.com",
|
||||
Password: "Wrobyak4",
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateOrganization() *f3.Organization {
|
||||
orgname := f.randomString("org")
|
||||
return &f3.Organization{
|
||||
FullName: orgname + " Lambda",
|
||||
Name: orgname,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateProject() *f3.Project {
|
||||
projectname := f.randomString("project")
|
||||
|
||||
return &f3.Project{
|
||||
Name: projectname,
|
||||
IsPrivate: false,
|
||||
IsMirror: false,
|
||||
Description: "project description",
|
||||
DefaultBranch: "master",
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateForkedProject(parent path.Path, forked string) *f3.Project {
|
||||
project := f.GenerateProject()
|
||||
project.Forked = f3.NewReference(forked)
|
||||
return project
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateRepository(name string) *f3.Repository {
|
||||
repository := &f3.Repository{
|
||||
Name: name,
|
||||
}
|
||||
p := f.t.TempDir()
|
||||
helper := tests_repository.NewTestHelper(f.t, p, nil)
|
||||
helper.CreateRepositoryContent("").PushMirror()
|
||||
repository.FetchFunc = func(ctx context.Context, destination string, internalRefs []string) {
|
||||
f.logger.Debug("%s %s", p, destination)
|
||||
helpers_repository.GitMirror(context.Background(), nil, p, destination, internalRefs)
|
||||
}
|
||||
return repository
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateReaction(parent path.Path) *f3.Reaction {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
return &f3.Reaction{
|
||||
UserID: f3_tree.NewUserReference(user.GetID()),
|
||||
Content: "heart",
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateMilestone() *f3.Milestone {
|
||||
now := now()
|
||||
created := tick(&now)
|
||||
updated := tick(&now)
|
||||
deadline := tick(&now)
|
||||
|
||||
return &f3.Milestone{
|
||||
Title: "milestone1",
|
||||
Description: "milestone1 description",
|
||||
Deadline: &deadline,
|
||||
Created: created,
|
||||
Updated: &updated,
|
||||
Closed: nil,
|
||||
State: f3.MilestoneStateOpen,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GeneratePullRequest(parent path.Path) *f3.PullRequest {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
projectNode := f3_tree.GetFirstNodeKind(parent.Last().(generic.NodeInterface), f3_tree.KindProject)
|
||||
repositoryNode := projectNode.Find(generic.NewPathFromString("repositories/vcs"))
|
||||
repositoryNode.Get(context.Background())
|
||||
repositoryHelper := tests_repository.NewTestHelper(f.t, "", repositoryNode)
|
||||
|
||||
mainRef := "master"
|
||||
mainSha := repositoryHelper.GetRepositorySha(mainRef)
|
||||
featureRef := "feature"
|
||||
repositoryHelper.InternalBranchRepositoryFeature(featureRef, "feature content")
|
||||
featureSha := repositoryHelper.GetRepositorySha(featureRef)
|
||||
f.logger.Debug("master %s at main %s feature %s", repositoryHelper.GetBare(), mainSha, featureSha)
|
||||
repositoryHelper.PushMirror()
|
||||
|
||||
now := now()
|
||||
prCreated := tick(&now)
|
||||
prUpdated := tick(&now)
|
||||
|
||||
return &f3.PullRequest{
|
||||
PosterID: f3_tree.NewUserReference(user.GetID()),
|
||||
Title: "pr title",
|
||||
Content: "pr content",
|
||||
State: f3.PullRequestStateOpen,
|
||||
IsLocked: false,
|
||||
Created: prCreated,
|
||||
Updated: prUpdated,
|
||||
Closed: nil,
|
||||
Merged: false,
|
||||
MergedTime: nil,
|
||||
MergeCommitSHA: "",
|
||||
Head: f3.PullRequestBranch{
|
||||
Ref: featureRef,
|
||||
SHA: featureSha,
|
||||
Repository: f3_tree.NewPullRequestSameRepositoryReference(),
|
||||
},
|
||||
Base: f3.PullRequestBranch{
|
||||
Ref: mainRef,
|
||||
SHA: mainSha,
|
||||
Repository: f3_tree.NewPullRequestSameRepositoryReference(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateLabel() *f3.Label {
|
||||
name := f.randomString("label")
|
||||
return &f3.Label{
|
||||
Name: name,
|
||||
Color: "ffffff",
|
||||
Description: name + " description",
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateTopic() *f3.Topic {
|
||||
return &f3.Topic{
|
||||
Name: "topic1",
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateIssue(parent path.Path) *f3.Issue {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
projectNode := f3_tree.GetFirstNodeKind(parent.Last().(generic.NodeInterface), f3_tree.KindProject)
|
||||
|
||||
labelsNode := projectNode.Find(generic.NewPathFromString("labels"))
|
||||
require.NotEqualValues(f.t, generic.NilNode, labelsNode)
|
||||
labels := labelsNode.GetChildren()
|
||||
require.NotEmpty(f.t, labels)
|
||||
firstLabel := labels[0]
|
||||
|
||||
milestonesNode := projectNode.Find(generic.NewPathFromString("milestones"))
|
||||
require.NotEqualValues(f.t, generic.NilNode, milestonesNode)
|
||||
milestones := milestonesNode.GetChildren()
|
||||
require.NotEmpty(f.t, milestones)
|
||||
firstMilestone := milestones[0]
|
||||
|
||||
now := now()
|
||||
updated := tick(&now)
|
||||
closed := tick(&now)
|
||||
created := tick(&now)
|
||||
|
||||
return &f3.Issue{
|
||||
PosterID: f3_tree.NewUserReference(user.GetID()),
|
||||
Assignees: []*f3.Reference{f3_tree.NewUserReference(user.GetID())},
|
||||
Labels: []*f3.Reference{f3_tree.NewIssueLabelReference(firstLabel.GetID())},
|
||||
Milestone: f3_tree.NewIssueMilestoneReference(firstMilestone.GetID()),
|
||||
Title: "title",
|
||||
Content: "content",
|
||||
State: f3.IssueStateOpen,
|
||||
IsLocked: false,
|
||||
Created: created,
|
||||
Updated: updated,
|
||||
Closed: &closed,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateComment(parent path.Path) *f3.Comment {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
|
||||
now := now()
|
||||
commentCreated := tick(&now)
|
||||
commentUpdated := tick(&now)
|
||||
|
||||
posterID := f3_tree.NewUserReference(user.GetID())
|
||||
|
||||
return &f3.Comment{
|
||||
PosterID: posterID,
|
||||
Created: commentCreated,
|
||||
Updated: commentUpdated,
|
||||
Content: "comment content",
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateRelease(parent path.Path) *f3.Release {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
project := f3_tree.GetFirstNodeKind(parent.Last().(generic.NodeInterface), f3_tree.KindProject)
|
||||
repository := project.Find(generic.NewPathFromString("repositories/vcs"))
|
||||
repository.Get(context.Background())
|
||||
repositoryHelper := tests_repository.NewTestHelper(f.t, "", repository)
|
||||
|
||||
now := now()
|
||||
releaseCreated := tick(&now)
|
||||
|
||||
tag := "releasetagv12"
|
||||
repositoryHelper.CreateRepositoryTag(tag, "master")
|
||||
sha := repositoryHelper.GetRepositorySha("master")
|
||||
repositoryHelper.PushMirror()
|
||||
|
||||
return &f3.Release{
|
||||
TagName: tag,
|
||||
TargetCommitish: sha,
|
||||
Name: "v12 name",
|
||||
Body: "v12 body",
|
||||
Draft: false,
|
||||
Prerelease: false,
|
||||
PublisherID: f3_tree.NewUserReference(user.GetID()),
|
||||
Created: releaseCreated,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateAsset(parent path.Path) *f3.ReleaseAsset {
|
||||
name := "assetname"
|
||||
content := "assetcontent"
|
||||
downloadURL := "downloadURL"
|
||||
now := now()
|
||||
assetCreated := tick(&now)
|
||||
|
||||
size := len(content)
|
||||
downloadCount := int64(10)
|
||||
sha256 := fmt.Sprintf("%x", sha256.Sum256([]byte(content)))
|
||||
|
||||
return &f3.ReleaseAsset{
|
||||
Name: name,
|
||||
Size: int64(size),
|
||||
DownloadCount: downloadCount,
|
||||
Created: assetCreated,
|
||||
SHA256: sha256,
|
||||
DownloadURL: downloadURL,
|
||||
DownloadFunc: func() io.ReadCloser {
|
||||
rc := io.NopCloser(strings.NewReader(content))
|
||||
return rc
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateReview(parent path.Path) *f3.Review {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
|
||||
projectNode := f3_tree.GetFirstNodeKind(parent.Last().(generic.NodeInterface), f3_tree.KindProject)
|
||||
repositoryNode := projectNode.Find(generic.NewPathFromString("repositories/vcs"))
|
||||
repositoryNode.Get(context.Background())
|
||||
repositoryHelper := tests_repository.NewTestHelper(f.t, "", repositoryNode)
|
||||
|
||||
now := now()
|
||||
reviewCreated := tick(&now)
|
||||
|
||||
featureSha := repositoryHelper.GetRepositorySha("feature")
|
||||
|
||||
return &f3.Review{
|
||||
ReviewerID: f3_tree.NewUserReference(user.GetID()),
|
||||
Official: true,
|
||||
CommitID: featureSha,
|
||||
Content: "the review content",
|
||||
CreatedAt: reviewCreated,
|
||||
State: f3.ReviewStateCommented,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Creator) GenerateReviewComment(parent path.Path) *f3.ReviewComment {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
|
||||
projectNode := f3_tree.GetFirstNodeKind(parent.Last().(generic.NodeInterface), f3_tree.KindProject)
|
||||
repositoryNode := projectNode.Find(generic.NewPathFromString("repositories/vcs"))
|
||||
repositoryNode.Get(context.Background())
|
||||
repositoryHelper := tests_repository.NewTestHelper(f.t, "", repositoryNode)
|
||||
|
||||
now := now()
|
||||
commentCreated := tick(&now)
|
||||
commentUpdated := tick(&now)
|
||||
|
||||
featureSha := repositoryHelper.GetRepositorySha("feature")
|
||||
|
||||
return &f3.ReviewComment{
|
||||
Content: "comment content",
|
||||
TreePath: "README.md",
|
||||
DiffHunk: "@@ -108,7 +108,6 @@",
|
||||
Line: 1,
|
||||
CommitID: featureSha,
|
||||
PosterID: f3_tree.NewUserReference(user.GetID()),
|
||||
CreatedAt: commentCreated,
|
||||
UpdatedAt: commentUpdated,
|
||||
}
|
||||
}
|
260
tree/tests/f3/f3_test.go
Normal file
260
tree/tests/f3/f3_test.go
Normal file
|
@ -0,0 +1,260 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package f3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.forgejo.org/f3/gof3/v3/f3"
|
||||
filesystem_options "code.forgejo.org/f3/gof3/v3/forges/filesystem/options"
|
||||
tests_repository "code.forgejo.org/f3/gof3/v3/forges/helpers/tests/repository"
|
||||
"code.forgejo.org/f3/gof3/v3/options"
|
||||
"code.forgejo.org/f3/gof3/v3/path"
|
||||
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||
tests_forge "code.forgejo.org/f3/gof3/v3/tree/tests/f3/forge"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestF3Mirror(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, factory := range tests_forge.GetFactories() {
|
||||
testForge := factory()
|
||||
t.Run(testForge.GetName(), func(t *testing.T) {
|
||||
// testCase.options will t.Skip if the forge instance is not up
|
||||
forgeWriteOptions := testForge.NewOptions(t)
|
||||
forgeReadOptions := testForge.NewOptions(t)
|
||||
forgeReadOptions.(options.URLInterface).SetURL(forgeWriteOptions.(options.URLInterface).GetURL())
|
||||
|
||||
fixtureTree := generic.GetFactory("f3")(ctx, tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t))
|
||||
fixtureTree.Trace("======= build fixture")
|
||||
TreeBuildPartial(t, "F3Mirror"+testForge.GetName(), testForge.GetKindExceptions(), forgeWriteOptions, fixtureTree)
|
||||
|
||||
fixtureTree.Trace("======= mirror fixture to forge")
|
||||
|
||||
forgeWriteTree := generic.GetFactory("f3")(ctx, forgeWriteOptions)
|
||||
generic.TreeMirror(ctx, fixtureTree, forgeWriteTree, generic.NewPathFromString(""), generic.NewMirrorOptions())
|
||||
|
||||
paths := []string{""}
|
||||
if testForge.GetName() != filesystem_options.Name {
|
||||
paths = []string{"/forge/users/10111", "/forge/users/20222"}
|
||||
}
|
||||
pathPairs := make([][2]path.Path, 0, 5)
|
||||
for _, p := range paths {
|
||||
p := generic.NewPathFromString(p)
|
||||
pathPairs = append(pathPairs, [2]path.Path{p, generic.TreePathRemap(ctx, fixtureTree, p, forgeWriteTree)})
|
||||
}
|
||||
|
||||
fixtureTree.Trace("======= read from forge")
|
||||
|
||||
forgeReadTree := generic.GetFactory("f3")(ctx, forgeReadOptions)
|
||||
forgeReadTree.WalkAndGet(ctx, generic.NewWalkOptions(nil))
|
||||
|
||||
fixtureTree.Trace("======= mirror forge to filesystem")
|
||||
|
||||
verificationTree := generic.GetFactory("f3")(ctx, tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t))
|
||||
|
||||
for _, pathPair := range pathPairs {
|
||||
generic.TreeMirror(ctx, forgeReadTree, verificationTree, pathPair[1], generic.NewMirrorOptions())
|
||||
}
|
||||
|
||||
fixtureTree.Trace("======= compare fixture with forge mirrored to filesystem")
|
||||
for _, pathPair := range pathPairs {
|
||||
assert.True(t, generic.TreeCompare(ctx, fixtureTree, pathPair[0], verificationTree, pathPair[1]))
|
||||
}
|
||||
|
||||
TreeDelete(t, testForge.GetNonTestUsers(), forgeWriteOptions, forgeWriteTree)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestF3Tree(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
verify := func(tree generic.TreeInterface, expected []string) {
|
||||
collected := make([]string, 0, 10)
|
||||
collect := func(ctx context.Context, path path.Path, node generic.NodeInterface) {
|
||||
path = path.Append(node)
|
||||
collected = append(collected, path.String())
|
||||
}
|
||||
tree.WalkAndGet(ctx, generic.NewWalkOptions(collect))
|
||||
sort.Strings(expected)
|
||||
sort.Strings(collected)
|
||||
assert.EqualValues(t, expected, collected)
|
||||
}
|
||||
|
||||
for _, testCase := range []struct {
|
||||
name string
|
||||
build func(tree generic.TreeInterface)
|
||||
operations func(tree generic.TreeInterface)
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "full tree",
|
||||
build: func(tree generic.TreeInterface) { TreeBuild(t, "F3", nil, tree) },
|
||||
operations: func(tree generic.TreeInterface) {},
|
||||
expected: []string{
|
||||
"",
|
||||
"/forge",
|
||||
"/forge/organizations",
|
||||
"/forge/organizations/3330001",
|
||||
"/forge/organizations/3330001/projects",
|
||||
"/forge/topics",
|
||||
"/forge/topics/14411441",
|
||||
"/forge/users",
|
||||
"/forge/users/10111",
|
||||
"/forge/users/10111/projects",
|
||||
"/forge/users/10111/projects/74823",
|
||||
"/forge/users/10111/projects/74823/issues",
|
||||
"/forge/users/10111/projects/74823/issues/1234567",
|
||||
"/forge/users/10111/projects/74823/issues/1234567/comments",
|
||||
"/forge/users/10111/projects/74823/issues/1234567/comments/1111999",
|
||||
"/forge/users/10111/projects/74823/issues/1234567/comments/1111999/reactions",
|
||||
"/forge/users/10111/projects/74823/issues/1234567/reactions",
|
||||
"/forge/users/10111/projects/74823/issues/1234567/reactions/1212",
|
||||
"/forge/users/10111/projects/74823/labels",
|
||||
"/forge/users/10111/projects/74823/labels/7777",
|
||||
"/forge/users/10111/projects/74823/labels/99999",
|
||||
"/forge/users/10111/projects/74823/milestones",
|
||||
"/forge/users/10111/projects/74823/milestones/7888",
|
||||
"/forge/users/10111/projects/74823/pull_requests",
|
||||
"/forge/users/10111/projects/74823/pull_requests/2222",
|
||||
"/forge/users/10111/projects/74823/pull_requests/2222/comments",
|
||||
"/forge/users/10111/projects/74823/pull_requests/2222/reactions",
|
||||
"/forge/users/10111/projects/74823/pull_requests/2222/reviews",
|
||||
"/forge/users/10111/projects/74823/pull_requests/2222/reviews/4593",
|
||||
"/forge/users/10111/projects/74823/pull_requests/2222/reviews/4593/reactions",
|
||||
"/forge/users/10111/projects/74823/pull_requests/2222/reviews/4593/reviewcomments",
|
||||
"/forge/users/10111/projects/74823/pull_requests/2222/reviews/4593/reviewcomments/9876543",
|
||||
"/forge/users/10111/projects/74823/releases",
|
||||
"/forge/users/10111/projects/74823/releases/123",
|
||||
"/forge/users/10111/projects/74823/releases/123/assets",
|
||||
"/forge/users/10111/projects/74823/releases/123/assets/585858",
|
||||
"/forge/users/10111/projects/74823/repositories",
|
||||
"/forge/users/10111/projects/74823/repositories/vcs",
|
||||
"/forge/users/20222",
|
||||
"/forge/users/20222/projects",
|
||||
"/forge/users/20222/projects/99099",
|
||||
"/forge/users/20222/projects/99099/issues",
|
||||
"/forge/users/20222/projects/99099/labels",
|
||||
"/forge/users/20222/projects/99099/milestones",
|
||||
"/forge/users/20222/projects/99099/pull_requests",
|
||||
"/forge/users/20222/projects/99099/releases",
|
||||
"/forge/users/20222/projects/99099/repositories",
|
||||
"/forge/users/20222/projects/99099/repositories/vcs",
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
tree := generic.GetFactory("f3")(ctx, tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t))
|
||||
|
||||
tree.Trace("======= BUILD")
|
||||
testCase.build(tree)
|
||||
tree.Trace("======= OPERATIONS")
|
||||
testCase.operations(tree)
|
||||
|
||||
tree.Trace("======= VERIFY")
|
||||
verify(tree, testCase.expected)
|
||||
|
||||
tree.Clear(ctx)
|
||||
|
||||
tree.Trace("======= VERIFY STEP 2")
|
||||
verify(tree, testCase.expected)
|
||||
|
||||
tree.Trace("======= COMPARE")
|
||||
otherTree := generic.GetFactory("f3")(ctx, tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t))
|
||||
otherTree.GetOptions().(options.URLInterface).SetURL(tree.GetOptions().(options.URLInterface).GetURL())
|
||||
otherTree.WalkAndGet(ctx, generic.NewWalkOptions(nil))
|
||||
assert.True(t, generic.TreeCompare(ctx, tree, generic.NewPathFromString(""), otherTree, generic.NewPathFromString("")))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestF3Repository(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
verify := func(tree generic.TreeInterface, name string) {
|
||||
repositoryPath := generic.NewPathFromString(filepath.Join("/forge/users/10111/projects/74823/repositories", name))
|
||||
found := false
|
||||
tree.Apply(ctx, repositoryPath, generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
|
||||
node.Get(ctx)
|
||||
node.List(ctx)
|
||||
if node.GetKind() != f3_tree.KindRepository {
|
||||
return
|
||||
}
|
||||
helper := tests_repository.NewTestHelper(t, "", node)
|
||||
helper.AssertRepositoryFileExists("README.md")
|
||||
if name == f3.RepositoryNameDefault {
|
||||
helper.AssertRepositoryTagExists("releasetagv12")
|
||||
helper.AssertRepositoryBranchExists("feature")
|
||||
}
|
||||
found = true
|
||||
}).SetWhere(generic.ApplyEachNode))
|
||||
assert.True(t, found)
|
||||
}
|
||||
|
||||
for _, name := range []string{f3.RepositoryNameDefault} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
tree := generic.GetFactory("f3")(ctx, tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t))
|
||||
|
||||
tree.Trace("======= BUILD")
|
||||
TreeBuild(t, "F3", nil, tree)
|
||||
|
||||
tree.Trace("======= VERIFY")
|
||||
verify(tree, name)
|
||||
|
||||
tree.Clear(ctx)
|
||||
tree.Trace("======= VERIFY STEP 2")
|
||||
verify(tree, name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestF3Asset(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
content := "OTHER CONTENT"
|
||||
expectedSHA256 := fmt.Sprintf("%x", sha256.Sum256([]byte(content)))
|
||||
opts := tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t)
|
||||
{
|
||||
tree := generic.GetFactory("f3")(ctx, opts)
|
||||
TreeBuild(t, "F3", nil, tree)
|
||||
|
||||
assetPath := generic.NewPathFromString("/forge/users/10111/projects/74823/releases/123/assets/585858")
|
||||
asset := tree.Find(assetPath)
|
||||
assert.False(t, asset.GetIsNil())
|
||||
|
||||
assetFormat := asset.ToFormat().(*f3.ReleaseAsset)
|
||||
assetFormat.DownloadFunc = func() io.ReadCloser {
|
||||
rc := io.NopCloser(strings.NewReader(content))
|
||||
return rc
|
||||
}
|
||||
|
||||
asset.FromFormat(assetFormat)
|
||||
asset.Upsert(ctx)
|
||||
assetFormat = asset.ToFormat().(*f3.ReleaseAsset)
|
||||
assert.EqualValues(t, expectedSHA256, assetFormat.SHA256)
|
||||
}
|
||||
{
|
||||
tree := generic.GetFactory("f3")(ctx, opts)
|
||||
tree.WalkAndGet(ctx, generic.NewWalkOptions(nil))
|
||||
|
||||
assetPath := generic.NewPathFromString("/forge/users/10111/projects/74823/releases/123/assets/585858")
|
||||
asset := tree.Find(assetPath)
|
||||
assert.False(t, asset.GetIsNil())
|
||||
|
||||
assetFormat := asset.ToFormat().(*f3.ReleaseAsset)
|
||||
assert.EqualValues(t, expectedSHA256, assetFormat.SHA256)
|
||||
}
|
||||
}
|
105
tree/tests/f3/filesystem_test.go
Normal file
105
tree/tests/f3/filesystem_test.go
Normal file
|
@ -0,0 +1,105 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package f3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
filesystem_options "code.forgejo.org/f3/gof3/v3/forges/filesystem/options"
|
||||
"code.forgejo.org/f3/gof3/v3/id"
|
||||
"code.forgejo.org/f3/gof3/v3/options"
|
||||
"code.forgejo.org/f3/gof3/v3/path"
|
||||
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||
tests_forge "code.forgejo.org/f3/gof3/v3/tree/tests/f3/forge"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestF3FilesystemMappedID(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
//
|
||||
// aTree only has /forge/user/10111
|
||||
//
|
||||
aTree := generic.GetFactory("f3")(ctx, tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t))
|
||||
aDir := aTree.GetOptions().(options.URLInterface).GetURL()
|
||||
|
||||
creator := NewCreator(t, "F3", aTree.GetLogger())
|
||||
|
||||
aF3Tree := aTree.(f3_tree.TreeInterface)
|
||||
|
||||
userID := "10111"
|
||||
|
||||
aF3Tree.CreateChild(ctx, "/", func(parent path.Path, forge generic.NodeInterface) {
|
||||
forge.FromFormat(creator.GenerateForge())
|
||||
})
|
||||
|
||||
aF3Tree.CreateChild(ctx, "/forge/users", func(parent path.Path, user generic.NodeInterface) {
|
||||
user.FromFormat(GeneratorSetID(creator.GenerateUser(), userID))
|
||||
})
|
||||
|
||||
//
|
||||
// bTree mirrors aTree exactly
|
||||
//
|
||||
rootPath := generic.NewPathFromString("")
|
||||
|
||||
bTree := generic.GetFactory("f3")(ctx, tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t))
|
||||
|
||||
generic.TreeMirror(ctx, aTree, bTree, rootPath, generic.NewMirrorOptions())
|
||||
|
||||
assert.True(t, generic.TreeCompare(ctx, aTree, rootPath, bTree, rootPath))
|
||||
|
||||
//
|
||||
// aTree maps user id 10111 to 10111.mapped
|
||||
//
|
||||
userPath := generic.NewPathFromString(filepath.Join("/forge/users", userID))
|
||||
|
||||
userMappedID := id.NewNodeID("10111.mapped")
|
||||
|
||||
assert.True(t, aTree.Apply(ctx, userPath, generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
|
||||
node.SetMappedID(userMappedID)
|
||||
node.Upsert(ctx)
|
||||
})))
|
||||
|
||||
aTree = generic.GetFactory("f3")(ctx, tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t))
|
||||
aTree.GetOptions().(options.URLInterface).SetURL(aDir)
|
||||
|
||||
aTree.WalkAndGet(ctx, generic.NewWalkOptions(nil))
|
||||
assert.NotEqualValues(t, generic.NilNode, aTree.Find(userPath))
|
||||
|
||||
//
|
||||
// cTree mirrors aTree with user id 10111 remapped to 10111.mapped
|
||||
//
|
||||
cTree := generic.GetFactory("f3")(ctx, tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t))
|
||||
cDir := cTree.GetOptions().(options.URLInterface).GetURL()
|
||||
|
||||
generic.TreeMirror(ctx, aTree, cTree, rootPath, generic.NewMirrorOptions())
|
||||
|
||||
userMappedPath := generic.NewPathFromString(filepath.Join("/forge/users", userMappedID.String()))
|
||||
|
||||
assert.NotEqualValues(t, generic.NilNode, cTree.Find(userMappedPath))
|
||||
assert.EqualValues(t, generic.NilNode, cTree.Find(userPath))
|
||||
|
||||
//
|
||||
// reset cTree and read from the filesystem
|
||||
//
|
||||
cTree = generic.GetFactory("f3")(ctx, tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t))
|
||||
cTree.GetOptions().(options.URLInterface).SetURL(cDir)
|
||||
|
||||
cTree.WalkAndGet(ctx, generic.NewWalkOptions(nil))
|
||||
|
||||
assert.NotEqualValues(t, generic.NilNode, cTree.Find(userMappedPath))
|
||||
assert.EqualValues(t, generic.NilNode, cTree.Find(userPath))
|
||||
|
||||
//
|
||||
// delete aTree user
|
||||
//
|
||||
deleted := aTree.Find(userPath).Delete(ctx)
|
||||
assert.EqualValues(t, userMappedID, deleted.GetMappedID())
|
||||
assert.EqualValues(t, generic.NilNode, cTree.Find(userPath))
|
||||
}
|
224
tree/tests/f3/fixture.go
Normal file
224
tree/tests/f3/fixture.go
Normal file
|
@ -0,0 +1,224 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package f3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"code.forgejo.org/f3/gof3/v3/f3"
|
||||
"code.forgejo.org/f3/gof3/v3/kind"
|
||||
"code.forgejo.org/f3/gof3/v3/options"
|
||||
"code.forgejo.org/f3/gof3/v3/path"
|
||||
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||
)
|
||||
|
||||
var KindToFixturePath = map[kind.Kind]string{
|
||||
f3_tree.KindTopics: "/forge/topics",
|
||||
f3_tree.KindTopic: "/forge/topics/14411441",
|
||||
f3_tree.KindUsers: "/forge/users",
|
||||
f3_tree.KindUser: "/forge/users/10111",
|
||||
f3_tree.KindProjects: "/forge/users/10111/projects",
|
||||
f3_tree.KindProject: "/forge/users/10111/projects/74823",
|
||||
f3_tree.KindLabels: "/forge/users/10111/projects/74823/labels",
|
||||
f3_tree.KindLabel: "/forge/users/10111/projects/74823/labels/7777",
|
||||
f3_tree.KindIssues: "/forge/users/10111/projects/74823/issues",
|
||||
f3_tree.KindIssue: "/forge/users/10111/projects/74823/issues/1234567",
|
||||
f3_tree.KindPullRequests: "/forge/users/10111/projects/74823/pull_requests",
|
||||
f3_tree.KindPullRequest: "/forge/users/10111/projects/74823/pull_requests/2222",
|
||||
f3_tree.KindReviews: "/forge/users/10111/projects/74823/pull_requests/2222/reviews",
|
||||
f3_tree.KindReview: "/forge/users/10111/projects/74823/pull_requests/2222/reviews/4593",
|
||||
f3_tree.KindReviewComments: "/forge/users/10111/projects/74823/pull_requests/2222/reviews/4593/reviewcomments",
|
||||
f3_tree.KindReviewComment: "/forge/users/10111/projects/74823/pull_requests/2222/reviews/4593/reviewcomments/9876543",
|
||||
f3_tree.KindMilestones: "/forge/users/10111/projects/74823/milestones",
|
||||
f3_tree.KindMilestone: "/forge/users/10111/projects/74823/milestones/7888",
|
||||
f3_tree.KindReactions: "/forge/users/10111/projects/74823/issues/1234567/reactions",
|
||||
f3_tree.KindReaction: "/forge/users/10111/projects/74823/issues/1234567/reactions/1212",
|
||||
f3_tree.KindComments: "/forge/users/10111/projects/74823/issues/1234567/comments",
|
||||
f3_tree.KindComment: "/forge/users/10111/projects/74823/issues/1234567/comments/1111999",
|
||||
f3_tree.KindRepositories: "/forge/users/10111/projects/74823/repositories",
|
||||
f3_tree.KindRepository: "/forge/users/10111/projects/74823/repositories/vcs",
|
||||
f3_tree.KindReleases: "/forge/users/10111/projects/74823/releases",
|
||||
f3_tree.KindRelease: "/forge/users/10111/projects/74823/releases/123",
|
||||
f3_tree.KindAssets: "/forge/users/10111/projects/74823/releases/123/assets",
|
||||
f3_tree.KindAsset: "/forge/users/10111/projects/74823/releases/123/assets/585858",
|
||||
f3_tree.KindOrganizations: "/forge/organizations",
|
||||
f3_tree.KindOrganization: "/forge/organizations/3330001",
|
||||
}
|
||||
|
||||
var KindWithFixturePath = SetKindWithFixturePath()
|
||||
|
||||
func SetKindWithFixturePath() []kind.Kind {
|
||||
l := make([]kind.Kind, 0, len(KindToFixturePath))
|
||||
|
||||
for kind := range KindToFixturePath {
|
||||
l = append(l, kind)
|
||||
}
|
||||
sort.Slice(l, func(i, j int) bool { return string(l[i]) < string(l[j]) })
|
||||
return l
|
||||
}
|
||||
|
||||
func TreeBuild(t *testing.T, name string, opts options.Interface, tree generic.TreeInterface) {
|
||||
TreeBuildPartial(t, name, []kind.Kind{}, opts, tree)
|
||||
}
|
||||
|
||||
func TreeBuildPartial(t *testing.T, name string, exceptions []kind.Kind, opts options.Interface, tree generic.TreeInterface) {
|
||||
ctx := context.Background()
|
||||
|
||||
creator := NewCreator(t, name, tree.GetLogger())
|
||||
|
||||
f3Tree := tree.(f3_tree.TreeInterface)
|
||||
url := "<unknown>"
|
||||
if urlInterface, ok := opts.(options.URLInterface); ok {
|
||||
url = urlInterface.GetURL()
|
||||
}
|
||||
|
||||
f3Tree.CreateChild(ctx, "/", func(parent path.Path, forge generic.NodeInterface) {
|
||||
f := creator.GenerateForge()
|
||||
f.URL = url
|
||||
forge.FromFormat(f)
|
||||
})
|
||||
|
||||
if slices.Contains(exceptions, f3_tree.KindUsers) {
|
||||
return
|
||||
}
|
||||
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindUsers], func(parent path.Path, user generic.NodeInterface) {
|
||||
user.FromFormat(GeneratorSetID(creator.GenerateUser(), "10111"))
|
||||
})
|
||||
|
||||
if slices.Contains(exceptions, f3_tree.KindProjects) {
|
||||
return
|
||||
}
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindProjects], func(parent path.Path, project generic.NodeInterface) {
|
||||
project.FromFormat(GeneratorSetID(creator.GenerateProject(), "74823"))
|
||||
})
|
||||
|
||||
if slices.Contains(exceptions, f3_tree.KindRepositories) {
|
||||
return
|
||||
}
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindRepositories], func(parent path.Path, repository generic.NodeInterface) {
|
||||
repository.FromFormat(GeneratorSetID(creator.GenerateRepository(f3.RepositoryNameDefault), f3.RepositoryNameDefault))
|
||||
})
|
||||
|
||||
if !slices.Contains(exceptions, f3_tree.KindReleases) {
|
||||
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindReleases], func(parent path.Path, release generic.NodeInterface) {
|
||||
release.FromFormat(GeneratorSetID(creator.GenerateRelease(parent), "123"))
|
||||
})
|
||||
|
||||
if !slices.Contains(exceptions, f3_tree.KindAssets) {
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindAssets], func(parent path.Path, asset generic.NodeInterface) {
|
||||
asset.FromFormat(GeneratorSetID(creator.GenerateAsset(parent), "585858"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
reviewerID := "20222"
|
||||
{
|
||||
userID := "20222"
|
||||
f3Tree.CreateChild(ctx, "/forge/users", func(parent path.Path, user generic.NodeInterface) {
|
||||
user.FromFormat(GeneratorSetID(creator.GenerateUser(), userID))
|
||||
})
|
||||
projectID := "99099"
|
||||
f3Tree.CreateChild(ctx, filepath.Join("/forge/users", userID, "projects"), func(parent path.Path, user generic.NodeInterface) {
|
||||
user.FromFormat(GeneratorSetID(creator.GenerateForkedProject(parent, KindToFixturePath[f3_tree.KindProject]), projectID))
|
||||
})
|
||||
f3Tree.CreateChild(ctx, filepath.Join("/forge/users", userID, "projects", projectID, "repositories"), func(parent path.Path, repository generic.NodeInterface) {
|
||||
repository.FromFormat(GeneratorSetID(creator.GenerateRepository(f3.RepositoryNameDefault), f3.RepositoryNameDefault))
|
||||
})
|
||||
}
|
||||
|
||||
if !slices.Contains(exceptions, f3_tree.KindPullRequests) {
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindPullRequests], func(parent path.Path, pullRequest generic.NodeInterface) {
|
||||
pullRequest.FromFormat(GeneratorSetID(creator.GeneratePullRequest(parent), "2222"))
|
||||
})
|
||||
|
||||
if !slices.Contains(exceptions, f3_tree.KindReviews) {
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindReviews], func(parent path.Path, review generic.NodeInterface) {
|
||||
reviewFormat := creator.GenerateReview(parent)
|
||||
GeneratorSetID(reviewFormat, "4593")
|
||||
reviewFormat.ReviewerID = f3_tree.NewUserReference(reviewerID)
|
||||
review.FromFormat(reviewFormat)
|
||||
})
|
||||
|
||||
if !slices.Contains(exceptions, f3_tree.KindReviewComments) {
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindReviewComments], func(parent path.Path, reviewComment generic.NodeInterface) {
|
||||
reviewCommentFormat := creator.GenerateReviewComment(parent)
|
||||
GeneratorSetID(reviewCommentFormat, "9876543")
|
||||
reviewCommentFormat.PosterID = f3_tree.NewUserReference(reviewerID)
|
||||
reviewComment.FromFormat(reviewCommentFormat)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !slices.Contains(exceptions, f3_tree.KindLabels) {
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindLabels], func(parent path.Path, label generic.NodeInterface) {
|
||||
label.FromFormat(GeneratorSetID(creator.GenerateLabel(), "99999"))
|
||||
})
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindLabels], func(parent path.Path, label generic.NodeInterface) {
|
||||
label.FromFormat(GeneratorSetID(creator.GenerateLabel(), "7777"))
|
||||
})
|
||||
}
|
||||
|
||||
if !slices.Contains(exceptions, f3_tree.KindMilestones) {
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindMilestones], func(parent path.Path, milestone generic.NodeInterface) {
|
||||
milestone.FromFormat(GeneratorSetID(creator.GenerateMilestone(), "7888"))
|
||||
})
|
||||
}
|
||||
|
||||
if !slices.Contains(exceptions, f3_tree.KindIssues) {
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindIssues], func(parent path.Path, issue generic.NodeInterface) {
|
||||
issue.FromFormat(GeneratorSetID(creator.GenerateIssue(parent), "1234567"))
|
||||
})
|
||||
|
||||
if !slices.Contains(exceptions, f3_tree.KindComments) {
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindComments], func(parent path.Path, comment generic.NodeInterface) {
|
||||
comment.FromFormat(GeneratorSetID(creator.GenerateComment(parent), "1111999"))
|
||||
})
|
||||
|
||||
if !slices.Contains(exceptions, f3_tree.KindReactions) {
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindReactions], func(parent path.Path, reaction generic.NodeInterface) {
|
||||
reaction.FromFormat(GeneratorSetID(creator.GenerateReaction(parent), "1212"))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !slices.Contains(exceptions, f3_tree.KindOrganizations) {
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindOrganizations], func(parent path.Path, organization generic.NodeInterface) {
|
||||
organization.FromFormat(GeneratorSetID(creator.GenerateOrganization(), "3330001"))
|
||||
})
|
||||
}
|
||||
|
||||
if !slices.Contains(exceptions, f3_tree.KindTopics) {
|
||||
f3Tree.CreateChild(ctx, KindToFixturePath[f3_tree.KindTopics], func(parent path.Path, topic generic.NodeInterface) {
|
||||
topic.FromFormat(GeneratorSetID(creator.GenerateTopic(), "14411441"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TreeDelete(t *testing.T, nonTestUsers []string, options options.Interface, tree generic.TreeInterface) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, owners := range []path.Path{f3_tree.OrganizationsPath, f3_tree.UsersPath} {
|
||||
for _, owner := range tree.Find(owners).List(ctx) {
|
||||
if user, ok := owner.ToFormat().(*f3.User); ok {
|
||||
if slices.Contains(nonTestUsers, user.UserName) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, project := range owner.Find(generic.NewPathFromString(f3_tree.KindProjects)).List(ctx) {
|
||||
project.Delete(ctx)
|
||||
}
|
||||
owner.Delete(ctx)
|
||||
}
|
||||
}
|
||||
}
|
30
tree/tests/f3/forge/base.go
Normal file
30
tree/tests/f3/forge/base.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package forge
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"code.forgejo.org/f3/gof3/v3/kind"
|
||||
)
|
||||
|
||||
const (
|
||||
ComplianceNameForkedPullRequest = "forked_pull_request"
|
||||
)
|
||||
|
||||
type Base struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (o *Base) GetName() string { return o.name }
|
||||
func (o *Base) SetName(name string) { o.name = name }
|
||||
func (o *Base) GetNonTestUsers() []string { return []string{} }
|
||||
|
||||
func (o *Base) GetKindExceptions() []kind.Kind { return nil }
|
||||
func (o *Base) GetNameExceptions() []string { return nil }
|
||||
|
||||
func (o *Base) DeleteAfterCompliance() bool {
|
||||
return os.Getenv("GOF3_TEST_COMPLIANCE_CLEANUP") != "false"
|
||||
}
|
35
tree/tests/f3/forge/factory.go
Normal file
35
tree/tests/f3/forge/factory.go
Normal file
|
@ -0,0 +1,35 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package forge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type (
|
||||
Factory func() Interface
|
||||
Factories map[string]Factory
|
||||
)
|
||||
|
||||
var factories = make(Factories, 10)
|
||||
|
||||
func GetFactories() Factories {
|
||||
return factories
|
||||
}
|
||||
|
||||
func RegisterFactory(name string, factory Factory) {
|
||||
name = strings.ToLower(name)
|
||||
factories[name] = factory
|
||||
}
|
||||
|
||||
func GetFactory(name string) Factory {
|
||||
name = strings.ToLower(name)
|
||||
factory, ok := factories[name]
|
||||
if !ok {
|
||||
panic(fmt.Errorf("no factory registered for %s", name))
|
||||
}
|
||||
return factory
|
||||
}
|
26
tree/tests/f3/forge/interface.go
Normal file
26
tree/tests/f3/forge/interface.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package forge
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.forgejo.org/f3/gof3/v3/kind"
|
||||
"code.forgejo.org/f3/gof3/v3/options"
|
||||
)
|
||||
|
||||
type Interface interface {
|
||||
GetName() string
|
||||
SetName(string)
|
||||
|
||||
DeleteAfterCompliance() bool
|
||||
|
||||
GetKindExceptions() []kind.Kind
|
||||
GetNameExceptions() []string
|
||||
|
||||
GetNonTestUsers() []string
|
||||
|
||||
NewOptions(t *testing.T) options.Interface
|
||||
}
|
254
tree/tests/f3/forge_compliance.go
Normal file
254
tree/tests/f3/forge_compliance.go
Normal file
|
@ -0,0 +1,254 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package f3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.forgejo.org/f3/gof3/v3/f3"
|
||||
filesystem_options "code.forgejo.org/f3/gof3/v3/forges/filesystem/options"
|
||||
tests_repository "code.forgejo.org/f3/gof3/v3/forges/helpers/tests/repository"
|
||||
"code.forgejo.org/f3/gof3/v3/id"
|
||||
"code.forgejo.org/f3/gof3/v3/kind"
|
||||
"code.forgejo.org/f3/gof3/v3/path"
|
||||
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||
tests_forge "code.forgejo.org/f3/gof3/v3/tree/tests/f3/forge"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type remapper func(path string) path.Path
|
||||
|
||||
func ForgeCompliance(t *testing.T, name string) {
|
||||
testForge := tests_forge.GetFactory(name)()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
forgeOptions := testForge.NewOptions(t)
|
||||
forgeTree := generic.GetFactory("f3")(ctx, forgeOptions)
|
||||
|
||||
forgeTree.Trace("======= build fixture")
|
||||
fixtureTree := generic.GetFactory("f3")(ctx, tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t))
|
||||
TreeBuildPartial(t, name, testForge.GetKindExceptions(), forgeOptions, fixtureTree)
|
||||
|
||||
forgeTree.Trace("======= mirror fixture to forge")
|
||||
|
||||
generic.TreeMirror(ctx, fixtureTree, forgeTree, generic.NewPathFromString(""), generic.NewMirrorOptions())
|
||||
|
||||
forgeTree.Trace("======= run compliance tests")
|
||||
|
||||
remap := func(p string) path.Path {
|
||||
return generic.TreePathRemap(ctx, fixtureTree, generic.NewPathFromString(p), forgeTree)
|
||||
}
|
||||
|
||||
kindToForgePath := make(map[kind.Kind]string, len(KindToFixturePath))
|
||||
for kind, path := range KindToFixturePath {
|
||||
remappedPath := remap(path)
|
||||
if remappedPath.Empty() {
|
||||
forgeTree.Trace("%s was not mirrored, ignored", path)
|
||||
continue
|
||||
}
|
||||
kindToForgePath[kind] = remappedPath.String()
|
||||
}
|
||||
|
||||
ComplianceKindTests(t, name, forgeTree.(f3_tree.TreeInterface), kindToForgePath, testForge.GetKindExceptions())
|
||||
ComplianceNameTests(t, name, forgeTree.(f3_tree.TreeInterface), remap, kindToForgePath, testForge.GetNameExceptions())
|
||||
|
||||
if testForge.DeleteAfterCompliance() {
|
||||
TreeDelete(t, testForge.GetNonTestUsers(), forgeOptions, forgeTree)
|
||||
}
|
||||
}
|
||||
|
||||
func ComplianceNameTests(t *testing.T, name string, tree f3_tree.TreeInterface, remap remapper, kindToForgePath map[kind.Kind]string, exceptions []string) {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
if !slices.Contains(exceptions, tests_forge.ComplianceNameForkedPullRequest) {
|
||||
t.Run(tests_forge.ComplianceNameForkedPullRequest, func(t *testing.T) {
|
||||
kind := kind.Kind(f3_tree.KindPullRequests)
|
||||
p := kindToForgePath[kind]
|
||||
parent := tree.Find(generic.NewPathFromString(p))
|
||||
require.NotEqualValues(t, generic.NilNode, parent, p)
|
||||
child := tree.Factory(ctx, tree.GetChildrenKind(kind))
|
||||
childFormat := ComplianceForkedPullRequest(t, tree, remap, child.NewFormat().(*f3.PullRequest), parent.GetCurrentPath())
|
||||
|
||||
child.FromFormat(childFormat)
|
||||
tree.Trace("'Upsert' the new forked pull request in the parent and store it in the forge")
|
||||
child.SetParent(parent)
|
||||
child.Upsert(ctx)
|
||||
|
||||
tree.Trace("'Delete' child forked pull request")
|
||||
child.Delete(ctx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func ComplianceForkedPullRequest(t *testing.T, tree f3_tree.TreeInterface, remap remapper, pullRequest *f3.PullRequest, parent path.Path) *f3.PullRequest {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
projectNode := f3_tree.GetFirstNodeKind(parent.Last().(generic.NodeInterface), f3_tree.KindProject)
|
||||
repositoryNode := projectNode.Find(generic.NewPathFromString("repositories/vcs"))
|
||||
repositoryNode.Get(context.Background())
|
||||
repositoryHelper := tests_repository.NewTestHelper(t, "", repositoryNode)
|
||||
|
||||
mainRef := "master"
|
||||
mainSha := repositoryHelper.GetRepositorySha(mainRef)
|
||||
|
||||
tree.Trace("create a feature branch in the /forge/users/20222/projects/99099 fork")
|
||||
forkedRepositoryPath := remap("/forge/users/20222/projects/99099/repositories/vcs")
|
||||
forkedRepositoryNode := tree.Find(forkedRepositoryPath)
|
||||
require.NotEqual(t, forkedRepositoryNode, generic.NilNode)
|
||||
forkedRepositoryHelper := tests_repository.NewTestHelper(t, "", forkedRepositoryNode)
|
||||
featureRef := "generatedforkfeature"
|
||||
forkedRepositoryHelper.InternalBranchRepositoryFeature(featureRef, featureRef+" content")
|
||||
featureSha := forkedRepositoryHelper.GetRepositorySha(featureRef)
|
||||
forkedRepositoryHelper.PushMirror()
|
||||
|
||||
now := now()
|
||||
prCreated := tick(&now)
|
||||
prUpdated := tick(&now)
|
||||
|
||||
pullRequest.PosterID = f3_tree.NewUserReference(user.GetID())
|
||||
pullRequest.Title = featureRef + " pr title"
|
||||
pullRequest.Content = featureRef + " pr content"
|
||||
pullRequest.State = f3.PullRequestStateOpen
|
||||
pullRequest.IsLocked = false
|
||||
pullRequest.Created = prCreated
|
||||
pullRequest.Updated = prUpdated
|
||||
pullRequest.Closed = nil
|
||||
pullRequest.Merged = false
|
||||
pullRequest.MergedTime = nil
|
||||
pullRequest.MergeCommitSHA = ""
|
||||
pullRequest.Head = f3.PullRequestBranch{
|
||||
Ref: featureRef,
|
||||
SHA: featureSha,
|
||||
Repository: f3.NewReference(forkedRepositoryPath.String()),
|
||||
}
|
||||
pullRequest.Base = f3.PullRequestBranch{
|
||||
Ref: mainRef,
|
||||
SHA: mainSha,
|
||||
Repository: f3.NewReference("../../repository/vcs"),
|
||||
}
|
||||
return pullRequest
|
||||
}
|
||||
|
||||
func ComplianceKindTests(t *testing.T, name string, tree f3_tree.TreeInterface, kindToForgePath map[kind.Kind]string, exceptions []kind.Kind) {
|
||||
t.Helper()
|
||||
exceptions = append(exceptions, f3_tree.KindRepositories)
|
||||
|
||||
for _, kind := range KindWithFixturePath {
|
||||
path := kindToForgePath[kind]
|
||||
if !tree.IsContainer(kind) {
|
||||
continue
|
||||
}
|
||||
if slices.Contains(exceptions, kind) {
|
||||
continue
|
||||
}
|
||||
t.Run(string(kind), func(t *testing.T) {
|
||||
Compliance(t, name, tree, path, kind, GeneratorSetRandom, GeneratorModify)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Compliance(t *testing.T, name string, tree f3_tree.TreeInterface, p string, kind kind.Kind, generator GeneratorFunc, modificator ModificatorFunc) {
|
||||
t.Helper()
|
||||
ctx := context.Background()
|
||||
|
||||
tree.Trace("%s", p)
|
||||
parent := tree.Find(generic.NewPathFromString(p))
|
||||
require.NotEqualValues(t, generic.NilNode, parent, p)
|
||||
tree.Trace("create a new child in memory")
|
||||
child := tree.Factory(ctx, tree.GetChildrenKind(kind))
|
||||
childFormat := generator(t, name, child.NewFormat(), parent.GetCurrentPath())
|
||||
child.FromFormat(childFormat)
|
||||
if i := child.GetID(); i != id.NilID {
|
||||
tree.Trace("about to insert child %s", i)
|
||||
assert.EqualValues(t, generic.NilNode, parent.GetChild(child.GetID()))
|
||||
} else {
|
||||
tree.Trace("about to insert child with nil ID")
|
||||
}
|
||||
if child.GetDriver().IsNull() {
|
||||
t.Skip("no driver, skipping")
|
||||
}
|
||||
|
||||
tree.Trace("'Upsert' the new child in the parent and store it in the forge")
|
||||
child.SetParent(parent)
|
||||
child.Upsert(ctx)
|
||||
tree.Trace("done inserting child '%s'", child.GetID())
|
||||
before := child.ToFormat()
|
||||
require.EqualValues(t, before.GetID(), child.GetID().String())
|
||||
tree.Trace("'Get' the child '%s' from the forge", child.GetID())
|
||||
child.Get(ctx)
|
||||
after := child.ToFormat()
|
||||
tree.Trace("check the F3 representations Upsert & Get to/from the forge are equivalent")
|
||||
require.True(t, cmp.Equal(before, after), cmp.Diff(before, after))
|
||||
|
||||
tree.Trace("check the F3 representation FromFormat/ToFormat are identical")
|
||||
{
|
||||
saved := after
|
||||
|
||||
a := childFormat.Clone()
|
||||
if tree.AllocateID() {
|
||||
a.SetID("123456")
|
||||
}
|
||||
child.FromFormat(a)
|
||||
b := child.ToFormat()
|
||||
require.True(t, cmp.Equal(a, b), cmp.Diff(a, b))
|
||||
|
||||
child.FromFormat(saved)
|
||||
}
|
||||
|
||||
if childFormat.GetName() != childFormat.GetID() {
|
||||
tree.Trace("'GetIDFromName' %s %s", kind, childFormat.GetName())
|
||||
id := parent.GetIDFromName(ctx, childFormat.GetName())
|
||||
assert.EqualValues(t, child.GetID(), id)
|
||||
}
|
||||
|
||||
for i, modified := range modificator(t, after, parent.GetCurrentPath()) {
|
||||
tree.Trace("%d: %s 'Upsert' a modified child %v", i, kind, modified)
|
||||
child.FromFormat(modified)
|
||||
child.Upsert(ctx)
|
||||
tree.Trace("%d: 'Get' the modified child '%s' from the forge", i, child.GetID())
|
||||
child.Get(ctx)
|
||||
after = child.ToFormat()
|
||||
tree.Trace("%d: check the F3 representations Upsert & Get to/from the forge of the modified child are equivalent", i)
|
||||
require.True(t, cmp.Equal(modified, after), cmp.Diff(modified, after))
|
||||
}
|
||||
|
||||
nodeChildren := parent.GetNodeChildren()
|
||||
tree.Trace("'ListPage' and only 'Get' known %d children of %s", len(nodeChildren), parent.GetKind())
|
||||
if len(nodeChildren) > 0 {
|
||||
parent.List(ctx)
|
||||
for _, child := range parent.GetChildren() {
|
||||
if _, ok := nodeChildren[child.GetID()]; ok {
|
||||
tree.Trace("'WalkAndGet' %s child %s %s", parent.GetCurrentPath().ReadableString(), child.GetKind(), child.GetID())
|
||||
child.WalkAndGet(ctx, parent.GetCurrentPath(), generic.NewWalkOptions(nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tree.Trace("'Delete' child '%s' from the forge", child.GetID())
|
||||
child.Delete(ctx)
|
||||
assert.EqualValues(t, generic.NilNode, parent.GetChild(child.GetID()))
|
||||
|
||||
assert.True(t, child.GetIsSync())
|
||||
loop := 100
|
||||
for i := 0; i < loop; i++ {
|
||||
child.SetIsSync(false)
|
||||
child.Get(ctx)
|
||||
if !child.GetIsSync() {
|
||||
break
|
||||
}
|
||||
tree.Trace("waiting for asynchronous child deletion (%d/%d)", i, loop)
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
assert.False(t, child.GetIsSync())
|
||||
|
||||
tree.Trace("%s did something %s", kind, child)
|
||||
}
|
19
tree/tests/f3/forge_test.go
Normal file
19
tree/tests/f3/forge_test.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package f3
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
tests_forge "code.forgejo.org/f3/gof3/v3/tree/tests/f3/forge"
|
||||
)
|
||||
|
||||
func TestF3Forge(t *testing.T) {
|
||||
for name := range tests_forge.GetFactories() {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ForgeCompliance(t, name)
|
||||
})
|
||||
}
|
||||
}
|
474
tree/tests/f3/generator.go
Normal file
474
tree/tests/f3/generator.go
Normal file
|
@ -0,0 +1,474 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package f3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.forgejo.org/f3/gof3/v3/f3"
|
||||
tests_repository "code.forgejo.org/f3/gof3/v3/forges/helpers/tests/repository"
|
||||
"code.forgejo.org/f3/gof3/v3/path"
|
||||
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||
)
|
||||
|
||||
func GeneratorSetRandomID(id string, f f3.Interface, parent path.Path) f3.Interface {
|
||||
if parent.First().(generic.NodeInterface).GetTree().AllocateID() {
|
||||
return f
|
||||
}
|
||||
return GeneratorSetID(f, id)
|
||||
}
|
||||
|
||||
func GeneratorSetID(f f3.Interface, id string) f3.Interface {
|
||||
f.SetID(id)
|
||||
return f
|
||||
}
|
||||
|
||||
type ModificatorFunc func(t *testing.T, f f3.Interface, parent path.Path) []f3.Interface
|
||||
|
||||
func GeneratorModify(t *testing.T, f f3.Interface, parent path.Path) []f3.Interface {
|
||||
switch v := f.(type) {
|
||||
case *f3.User:
|
||||
return GeneratorModifyUser(v)
|
||||
case *f3.Organization:
|
||||
return GeneratorModifyOrganization(v)
|
||||
case *f3.Project:
|
||||
return GeneratorModifyProject(v, parent)
|
||||
case *f3.Issue:
|
||||
return GeneratorModifyIssue(v, parent)
|
||||
case *f3.Milestone:
|
||||
return GeneratorModifyMilestone(v, parent)
|
||||
case *f3.Topic:
|
||||
return GeneratorModifyTopic(v, parent)
|
||||
case *f3.Reaction:
|
||||
// a reaction cannot be modified, it can only be created and deleted
|
||||
return []f3.Interface{}
|
||||
case *f3.Label:
|
||||
return GeneratorModifyLabel(v, parent)
|
||||
case *f3.Comment:
|
||||
return GeneratorModifyComment(v, parent)
|
||||
case *f3.Release:
|
||||
return GeneratorModifyRelease(t, v, parent)
|
||||
case *f3.ReleaseAsset:
|
||||
return GeneratorModifyReleaseAsset(v, parent)
|
||||
case *f3.PullRequest:
|
||||
return GeneratorModifyPullRequest(t, v, parent)
|
||||
case *f3.Review:
|
||||
// a review cannot be modified, it can only be created and deleted
|
||||
return []f3.Interface{}
|
||||
case *f3.ReviewComment:
|
||||
return GeneratorModifyReviewComment(t, v, parent)
|
||||
default:
|
||||
panic(fmt.Errorf("not implemented %T", f))
|
||||
}
|
||||
}
|
||||
|
||||
type GeneratorFunc func(t *testing.T, name string, f f3.Interface, parent path.Path) f3.Interface
|
||||
|
||||
func GeneratorSetRandom(t *testing.T, name string, f f3.Interface, parent path.Path) f3.Interface {
|
||||
GeneratorSetRandomID(name, f, parent)
|
||||
switch v := f.(type) {
|
||||
case *f3.User:
|
||||
return GeneratorSetRandomUser(v)
|
||||
case *f3.Organization:
|
||||
return GeneratorSetRandomOrganization(v)
|
||||
case *f3.Project:
|
||||
return GeneratorSetRandomProject(v, parent)
|
||||
case *f3.Issue:
|
||||
return GeneratorSetRandomIssue(v, parent)
|
||||
case *f3.Milestone:
|
||||
return GeneratorSetRandomMilestone(v, parent)
|
||||
case *f3.Topic:
|
||||
return GeneratorSetRandomTopic(v, parent)
|
||||
case *f3.Reaction:
|
||||
return GeneratorSetRandomReaction(v, parent)
|
||||
case *f3.Label:
|
||||
return GeneratorSetRandomLabel(v, parent)
|
||||
case *f3.Comment:
|
||||
return GeneratorSetRandomComment(v, parent)
|
||||
case *f3.Release:
|
||||
return GeneratorSetRandomRelease(t, v, parent)
|
||||
case *f3.ReleaseAsset:
|
||||
return GeneratorSetRandomReleaseAsset(v, parent)
|
||||
case *f3.PullRequest:
|
||||
return GeneratorSetRandomPullRequest(t, v, parent)
|
||||
case *f3.Review:
|
||||
return GeneratorSetReview(t, v, parent)
|
||||
case *f3.ReviewComment:
|
||||
return GeneratorSetReviewComment(t, v, parent)
|
||||
default:
|
||||
panic(fmt.Errorf("not implemented %T", f))
|
||||
}
|
||||
}
|
||||
|
||||
func GeneratorSetRandomUser(user *f3.User) *f3.User {
|
||||
username := fmt.Sprintf("generateduser%s", user.GetID())
|
||||
user.Name = username + " Doe"
|
||||
user.UserName = username
|
||||
user.Email = username + "@example.com"
|
||||
user.Password = "Wrobyak4"
|
||||
return user
|
||||
}
|
||||
|
||||
func GeneratorModifyUser(user *f3.User) []f3.Interface {
|
||||
return []f3.Interface{user.Clone()}
|
||||
}
|
||||
|
||||
func GeneratorSetRandomOrganization(organization *f3.Organization) *f3.Organization {
|
||||
organizationname := fmt.Sprintf("generatedorg%s", organization.GetID())
|
||||
organization.FullName = organizationname + " Lambda"
|
||||
organization.Name = organizationname
|
||||
return organization
|
||||
}
|
||||
|
||||
func GeneratorModifyOrganization(organization *f3.Organization) []f3.Interface {
|
||||
organization0 := organization.Clone().(*f3.Organization)
|
||||
organization0.FullName = "modified " + organization.FullName
|
||||
return []f3.Interface{organization0}
|
||||
}
|
||||
|
||||
func GeneratorSetRandomProject(project *f3.Project, parent path.Path) *f3.Project {
|
||||
projectname := fmt.Sprintf("project%s", project.GetID())
|
||||
project.Name = projectname
|
||||
project.IsPrivate = false
|
||||
project.IsMirror = false
|
||||
project.Description = "project description"
|
||||
project.DefaultBranch = "main"
|
||||
return project
|
||||
}
|
||||
|
||||
func GeneratorModifyProject(project *f3.Project, parent path.Path) []f3.Interface {
|
||||
project0 := project.Clone().(*f3.Project)
|
||||
project0.Description = "modified " + project.Description
|
||||
return []f3.Interface{project0}
|
||||
}
|
||||
|
||||
func GeneratorSetRandomIssue(issue *f3.Issue, parent path.Path) *f3.Issue {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
projectNode := f3_tree.GetFirstNodeKind(parent.Last().(generic.NodeInterface), f3_tree.KindProject)
|
||||
labelsNode := projectNode.Find(generic.NewPathFromString("labels"))
|
||||
labels := labelsNode.GetChildren()
|
||||
firstLabel := labels[0]
|
||||
|
||||
now := now()
|
||||
updated := tick(&now)
|
||||
closed := tick(&now)
|
||||
created := tick(&now)
|
||||
|
||||
userRef := f3_tree.NewUserReference(user.GetID())
|
||||
labelRef := f3_tree.NewIssueLabelReference(firstLabel.GetID())
|
||||
milestonesNode := projectNode.Find(generic.NewPathFromString("milestones"))
|
||||
milestones := milestonesNode.GetChildren()
|
||||
firstMilestone := milestones[0]
|
||||
|
||||
issue.PosterID = userRef
|
||||
issue.Assignees = []*f3.Reference{userRef}
|
||||
issue.Labels = []*f3.Reference{labelRef}
|
||||
issue.Milestone = f3_tree.NewIssueMilestoneReference(firstMilestone.GetID())
|
||||
issue.Title = "title"
|
||||
issue.Content = "content"
|
||||
issue.State = f3.IssueStateOpen
|
||||
issue.IsLocked = false
|
||||
issue.Created = created
|
||||
issue.Updated = updated
|
||||
issue.Closed = &closed
|
||||
|
||||
return issue
|
||||
}
|
||||
|
||||
func GeneratorModifyIssue(issue *f3.Issue, parent path.Path) []f3.Interface {
|
||||
assignees := issue.Assignees
|
||||
milestone := issue.Milestone
|
||||
labels := issue.Labels
|
||||
|
||||
issue0 := issue.Clone().(*f3.Issue)
|
||||
issue0.Title = "modified " + issue.Title
|
||||
issue0.Content = "modified " + issue.Content
|
||||
|
||||
issueClosed := issue0.Clone().(*f3.Issue)
|
||||
issueClosed.Assignees = []*f3.Reference{}
|
||||
issueClosed.Milestone = &f3.Reference{}
|
||||
issueClosed.Labels = []*f3.Reference{}
|
||||
issueClosed.State = f3.IssueStateClosed
|
||||
issueClosed.IsLocked = true
|
||||
|
||||
issueOpen := issue0.Clone().(*f3.Issue)
|
||||
issueOpen.Assignees = assignees
|
||||
issueOpen.Milestone = milestone
|
||||
issueOpen.Labels = labels
|
||||
issueOpen.State = f3.IssueStateOpen
|
||||
issueClosed.IsLocked = false
|
||||
|
||||
return []f3.Interface{
|
||||
issue0,
|
||||
issueClosed,
|
||||
issueOpen,
|
||||
}
|
||||
}
|
||||
|
||||
func GeneratorSetRandomMilestone(milestone *f3.Milestone, parent path.Path) *f3.Milestone {
|
||||
now := now()
|
||||
created := tick(&now)
|
||||
updated := tick(&now)
|
||||
deadline := tick(&now)
|
||||
|
||||
title := fmt.Sprintf("milestone%s", milestone.GetID())
|
||||
milestone.Title = title
|
||||
milestone.Description = title + " description"
|
||||
milestone.Deadline = &deadline
|
||||
milestone.Created = created
|
||||
milestone.Updated = &updated
|
||||
milestone.Closed = nil
|
||||
milestone.State = f3.MilestoneStateOpen
|
||||
|
||||
return milestone
|
||||
}
|
||||
|
||||
func GeneratorModifyMilestone(milestone *f3.Milestone, parent path.Path) []f3.Interface {
|
||||
milestone0 := milestone.Clone().(*f3.Milestone)
|
||||
milestone0.Title = "modified " + milestone.Title
|
||||
|
||||
milestoneClosed := milestone0.Clone().(*f3.Milestone)
|
||||
milestoneClosed.State = f3.MilestoneStateClosed
|
||||
deadline := time.Now().Truncate(time.Second).Add(5 * time.Minute)
|
||||
milestoneClosed.Deadline = &deadline
|
||||
|
||||
milestoneOpen := milestone0.Clone().(*f3.Milestone)
|
||||
milestoneOpen.State = f3.MilestoneStateOpen
|
||||
|
||||
return []f3.Interface{
|
||||
milestone0,
|
||||
milestoneClosed,
|
||||
milestoneOpen,
|
||||
}
|
||||
}
|
||||
|
||||
func GeneratorSetRandomTopic(topic *f3.Topic, parent path.Path) *f3.Topic {
|
||||
topic.Name = fmt.Sprintf("topic%s", topic.GetID())
|
||||
return topic
|
||||
}
|
||||
|
||||
func GeneratorModifyTopic(topic *f3.Topic, parent path.Path) []f3.Interface {
|
||||
topic0 := topic.Clone().(*f3.Topic)
|
||||
return []f3.Interface{topic0}
|
||||
}
|
||||
|
||||
func GeneratorSetRandomReaction(reaction *f3.Reaction, parent path.Path) *f3.Reaction {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
reaction.UserID = f3_tree.NewUserReference(user.GetID())
|
||||
reaction.Content = "laugh"
|
||||
return reaction
|
||||
}
|
||||
|
||||
func GeneratorSetRandomLabel(label *f3.Label, parent path.Path) *f3.Label {
|
||||
name := fmt.Sprintf("label%s", label.GetID())
|
||||
label.Name = name
|
||||
label.Description = name + " description"
|
||||
label.Color = "ffffff"
|
||||
return label
|
||||
}
|
||||
|
||||
func GeneratorModifyLabel(label *f3.Label, parent path.Path) []f3.Interface {
|
||||
label0 := label.Clone().(*f3.Label)
|
||||
label0.Name = "modified" + label.Name
|
||||
label0.Color = "f0f0f0"
|
||||
label0.Description = "modified " + label.Description
|
||||
return []f3.Interface{label0}
|
||||
}
|
||||
|
||||
func GeneratorSetRandomComment(comment *f3.Comment, parent path.Path) *f3.Comment {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
|
||||
now := now()
|
||||
commentCreated := tick(&now)
|
||||
commentUpdated := tick(&now)
|
||||
|
||||
comment.PosterID = f3_tree.NewUserReference(user.GetID())
|
||||
comment.Created = commentCreated
|
||||
comment.Updated = commentUpdated
|
||||
comment.Content = "comment content"
|
||||
return comment
|
||||
}
|
||||
|
||||
func GeneratorModifyComment(comment *f3.Comment, parent path.Path) []f3.Interface {
|
||||
comment0 := comment.Clone().(*f3.Comment)
|
||||
comment0.Content = "modified" + comment.Content
|
||||
return []f3.Interface{comment0}
|
||||
}
|
||||
|
||||
func GeneratorSetRandomRelease(t *testing.T, release *f3.Release, parent path.Path) *f3.Release {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
project := f3_tree.GetFirstNodeKind(parent.Last().(generic.NodeInterface), f3_tree.KindProject)
|
||||
repository := project.Find(generic.NewPathFromString("repositories/vcs"))
|
||||
repository.Get(context.Background())
|
||||
repositoryHelper := tests_repository.NewTestHelper(t, "", repository)
|
||||
|
||||
now := now()
|
||||
releaseCreated := tick(&now)
|
||||
|
||||
tag := fmt.Sprintf("release%s", release.GetID())
|
||||
repositoryHelper.CreateRepositoryTag(tag, "master")
|
||||
sha := repositoryHelper.GetRepositorySha("master")
|
||||
fmt.Printf("GeneratorSetRandomRelease %s %s\n", repository.GetCurrentPath(), repository.GetID())
|
||||
repositoryHelper.PushMirror()
|
||||
|
||||
release.TagName = tag
|
||||
release.TargetCommitish = sha
|
||||
release.Name = tag + " name"
|
||||
release.Body = tag + " body"
|
||||
release.Draft = false
|
||||
release.Prerelease = false
|
||||
release.PublisherID = f3_tree.NewUserReference(user.GetID())
|
||||
release.Created = releaseCreated
|
||||
|
||||
return release
|
||||
}
|
||||
|
||||
func GeneratorModifyRelease(t *testing.T, release *f3.Release, parent path.Path) []f3.Interface {
|
||||
release0 := release.Clone().(*f3.Release)
|
||||
release0.Body = "modified " + release.Body
|
||||
return []f3.Interface{release0}
|
||||
}
|
||||
|
||||
func GeneratorSetRandomReleaseAsset(asset *f3.ReleaseAsset, parent path.Path) *f3.ReleaseAsset {
|
||||
name := fmt.Sprintf("assetname%s", asset.GetID())
|
||||
content := fmt.Sprintf("assetcontent%s", asset.GetID())
|
||||
downloadURL := "downloadURL"
|
||||
now := now()
|
||||
assetCreated := tick(&now)
|
||||
|
||||
size := len(content)
|
||||
downloadCount := int64(10)
|
||||
sha256 := fmt.Sprintf("%x", sha256.Sum256([]byte(content)))
|
||||
|
||||
asset.Name = name
|
||||
asset.Size = int64(size)
|
||||
asset.DownloadCount = downloadCount
|
||||
asset.Created = assetCreated
|
||||
asset.SHA256 = sha256
|
||||
asset.DownloadURL = downloadURL
|
||||
asset.DownloadFunc = func() io.ReadCloser {
|
||||
rc := io.NopCloser(strings.NewReader(content))
|
||||
return rc
|
||||
}
|
||||
|
||||
return asset
|
||||
}
|
||||
|
||||
func GeneratorModifyReleaseAsset(asset *f3.ReleaseAsset, parent path.Path) []f3.Interface {
|
||||
asset0 := asset.Clone().(*f3.ReleaseAsset)
|
||||
asset0.Name = "modified" + asset.Name
|
||||
return []f3.Interface{asset0}
|
||||
}
|
||||
|
||||
func GeneratorSetRandomPullRequest(t *testing.T, pullRequest *f3.PullRequest, parent path.Path) *f3.PullRequest {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
projectNode := f3_tree.GetFirstNodeKind(parent.Last().(generic.NodeInterface), f3_tree.KindProject)
|
||||
repositoryNode := projectNode.Find(generic.NewPathFromString("repositories/vcs"))
|
||||
repositoryNode.Get(context.Background())
|
||||
repositoryHelper := tests_repository.NewTestHelper(t, "", repositoryNode)
|
||||
|
||||
mainRef := "master"
|
||||
mainSha := repositoryHelper.GetRepositorySha(mainRef)
|
||||
featureRef := "generatedfeature"
|
||||
repositoryHelper.InternalBranchRepositoryFeature(featureRef, featureRef+" content")
|
||||
featureSha := repositoryHelper.GetRepositorySha(featureRef)
|
||||
fmt.Printf("createPullRequest: master %s at main %s feature %s\n", repositoryHelper.GetBare(), mainSha, featureSha)
|
||||
repositoryHelper.PushMirror()
|
||||
|
||||
now := now()
|
||||
prCreated := tick(&now)
|
||||
prUpdated := tick(&now)
|
||||
|
||||
pullRequest.PosterID = f3_tree.NewUserReference(user.GetID())
|
||||
pullRequest.Title = featureRef + " pr title"
|
||||
pullRequest.Content = featureRef + " pr content"
|
||||
pullRequest.State = f3.PullRequestStateOpen
|
||||
pullRequest.IsLocked = false
|
||||
pullRequest.Created = prCreated
|
||||
pullRequest.Updated = prUpdated
|
||||
pullRequest.Closed = nil
|
||||
pullRequest.Merged = false
|
||||
pullRequest.MergedTime = nil
|
||||
pullRequest.MergeCommitSHA = ""
|
||||
pullRequest.Head = f3.PullRequestBranch{
|
||||
Ref: featureRef,
|
||||
SHA: featureSha,
|
||||
Repository: f3.NewReference("../../repository/vcs"),
|
||||
}
|
||||
pullRequest.Base = f3.PullRequestBranch{
|
||||
Ref: mainRef,
|
||||
SHA: mainSha,
|
||||
Repository: f3.NewReference("../../repository/vcs"),
|
||||
}
|
||||
return pullRequest
|
||||
}
|
||||
|
||||
func GeneratorModifyPullRequest(t *testing.T, pullRequest *f3.PullRequest, parent path.Path) []f3.Interface {
|
||||
pullRequest0 := pullRequest.Clone().(*f3.PullRequest)
|
||||
pullRequest0.Title = "modified " + pullRequest.Title
|
||||
return []f3.Interface{pullRequest0}
|
||||
}
|
||||
|
||||
func GeneratorSetReview(t *testing.T, review *f3.Review, parent path.Path) *f3.Review {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
|
||||
projectNode := f3_tree.GetFirstNodeKind(parent.Last().(generic.NodeInterface), f3_tree.KindProject)
|
||||
repositoryNode := projectNode.Find(generic.NewPathFromString("repositories/vcs"))
|
||||
repositoryNode.Get(context.Background())
|
||||
repositoryHelper := tests_repository.NewTestHelper(t, "", repositoryNode)
|
||||
|
||||
now := now()
|
||||
reviewCreated := tick(&now)
|
||||
|
||||
featureSha := repositoryHelper.GetRepositorySha("feature")
|
||||
|
||||
review.ReviewerID = f3_tree.NewUserReference(user.GetID())
|
||||
review.Official = true
|
||||
review.CommitID = featureSha
|
||||
review.Content = "the review content"
|
||||
review.CreatedAt = reviewCreated
|
||||
review.State = f3.ReviewStateCommented
|
||||
|
||||
return review
|
||||
}
|
||||
|
||||
func GeneratorSetReviewComment(t *testing.T, comment *f3.ReviewComment, parent path.Path) *f3.ReviewComment {
|
||||
user := f3_tree.GetFirstFormat[*f3.User](parent.Last().(generic.NodeInterface))
|
||||
|
||||
projectNode := f3_tree.GetFirstNodeKind(parent.Last().(generic.NodeInterface), f3_tree.KindProject)
|
||||
repositoryNode := projectNode.Find(generic.NewPathFromString("repositories/vcs"))
|
||||
repositoryNode.Get(context.Background())
|
||||
repositoryHelper := tests_repository.NewTestHelper(t, "", repositoryNode)
|
||||
|
||||
now := now()
|
||||
commentCreated := tick(&now)
|
||||
commentUpdated := tick(&now)
|
||||
|
||||
featureSha := repositoryHelper.GetRepositorySha("feature")
|
||||
|
||||
comment.Content = "comment content"
|
||||
comment.TreePath = "README.md"
|
||||
comment.DiffHunk = "@@ -108,7 +108,6 @@"
|
||||
comment.Line = 1
|
||||
comment.CommitID = featureSha
|
||||
comment.PosterID = f3_tree.NewUserReference(user.GetID())
|
||||
comment.CreatedAt = commentCreated
|
||||
comment.UpdatedAt = commentUpdated
|
||||
|
||||
return comment
|
||||
}
|
||||
|
||||
func GeneratorModifyReviewComment(t *testing.T, comment *f3.ReviewComment, parent path.Path) []f3.Interface {
|
||||
comment0 := comment.Clone().(*f3.ReviewComment)
|
||||
comment0.Content = "modified " + comment.Content
|
||||
return []f3.Interface{comment0}
|
||||
}
|
125
tree/tests/f3/helpers_repository_test.go
Normal file
125
tree/tests/f3/helpers_repository_test.go
Normal file
|
@ -0,0 +1,125 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package f3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
filesystem_options "code.forgejo.org/f3/gof3/v3/forges/filesystem/options"
|
||||
forgejo_options "code.forgejo.org/f3/gof3/v3/forges/forgejo/options"
|
||||
helpers_repository "code.forgejo.org/f3/gof3/v3/forges/helpers/repository"
|
||||
tests_repository "code.forgejo.org/f3/gof3/v3/forges/helpers/tests/repository"
|
||||
"code.forgejo.org/f3/gof3/v3/logger"
|
||||
f3_tree "code.forgejo.org/f3/gof3/v3/tree/f3"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||
tests_forge "code.forgejo.org/f3/gof3/v3/tree/tests/f3/forge"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNodeRepositoryDriverMirrorNoop(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
url := t.TempDir()
|
||||
repositoryHelper := tests_repository.NewTestHelper(t, url, nil)
|
||||
repositoryHelper.CreateRepositoryContent("").PushMirror()
|
||||
log := logger.NewCaptureLogger()
|
||||
log.SetLevel(logger.Trace)
|
||||
|
||||
log.Reset()
|
||||
helpers_repository.GitMirror(ctx, log, url, url, []string{})
|
||||
assert.Contains(t, log.String(), "do nothing")
|
||||
|
||||
log.Reset()
|
||||
helpers_repository.GitMirrorRef(ctx, log, url, "fakeref", url, "fakeref")
|
||||
assert.Contains(t, log.String(), "do nothing")
|
||||
}
|
||||
|
||||
func TestNodeRepositoryDriverMirrorForgejo(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
log := logger.NewCaptureLogger()
|
||||
log.SetLevel(logger.Trace)
|
||||
|
||||
testForgejo := tests_forge.GetFactory(forgejo_options.Name)()
|
||||
opts := testForgejo.NewOptions(t)
|
||||
forgeTree := generic.GetFactory("f3")(ctx, opts)
|
||||
|
||||
forgeTree.Trace("======= build fixture")
|
||||
fixtureTree := generic.GetFactory("f3")(ctx, tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t))
|
||||
TreeBuildPartial(t, "F3", testForgejo.GetKindExceptions(), opts, fixtureTree)
|
||||
|
||||
forgeTree.Trace("======= mirror fixture to forge")
|
||||
|
||||
generic.TreeMirror(ctx, fixtureTree, forgeTree, generic.NewPathFromString(""), generic.NewMirrorOptions())
|
||||
|
||||
forgeTree.Trace("======= NodeRepositoryDriverMirror from a forgejo project to a directory")
|
||||
|
||||
repositoryPath := generic.TreePathRemap(ctx, fixtureTree, generic.NewPathFromString("/forge/users/10111/projects/74823/repositories/vcs"), forgeTree)
|
||||
require.False(t, repositoryPath.Empty())
|
||||
repository := forgeTree.Find(repositoryPath)
|
||||
repositoryURL := repository.GetDriver().(f3_tree.RepositoryNodeDriverProxyInterface).GetRepositoryURL()
|
||||
internalRefs := repository.GetDriver().(f3_tree.RepositoryNodeDriverProxyInterface).GetRepositoryInternalRefs()
|
||||
|
||||
log.Reset()
|
||||
destination := t.TempDir()
|
||||
tests_repository.NewTestHelper(t, destination, nil)
|
||||
helpers_repository.GitMirror(ctx, log, repositoryURL, destination, internalRefs)
|
||||
require.Contains(t, log.String(), "fetch fetchMirror")
|
||||
|
||||
forgeTree.Trace("======= NodeRepositoryDriverMirror from one forgejo project to another forgejo project")
|
||||
|
||||
otherRepositoryPath := generic.TreePathRemap(ctx, fixtureTree, generic.NewPathFromString("/forge/users/20222/projects/99099/repositories/vcs"), forgeTree)
|
||||
require.False(t, otherRepositoryPath.Empty())
|
||||
otherRepository := forgeTree.Find(otherRepositoryPath)
|
||||
otherRepositoryURL := otherRepository.GetDriver().(f3_tree.RepositoryNodeDriverProxyInterface).GetRepositoryURL()
|
||||
otherRepositoryPushURL := otherRepository.GetDriver().(f3_tree.RepositoryNodeDriverProxyInterface).GetRepositoryPushURL()
|
||||
otherInternalRefs := otherRepository.GetDriver().(f3_tree.RepositoryNodeDriverProxyInterface).GetRepositoryInternalRefs()
|
||||
|
||||
log.Reset()
|
||||
helpers_repository.GitMirror(ctx, log, repositoryURL, otherRepositoryPushURL, otherInternalRefs)
|
||||
require.Contains(t, log.String(), "+refs/")
|
||||
|
||||
forgeTree.Trace("======= NodeRepositoryDriverMirror from a directory to a forgejo project")
|
||||
|
||||
log.Reset()
|
||||
repositoryHelper := tests_repository.NewTestHelper(t, destination, nil)
|
||||
content := "SOMETHING DIFFERENT"
|
||||
repositoryHelper.CreateRepositoryContent(content).PushMirror()
|
||||
helpers_repository.GitMirror(ctx, log, destination, otherRepositoryPushURL, otherInternalRefs)
|
||||
require.Contains(t, log.String(), "+refs/")
|
||||
verificationDir := t.TempDir()
|
||||
repositoryHelper = tests_repository.NewTestHelper(t, verificationDir, nil)
|
||||
helpers_repository.GitMirror(ctx, log, otherRepositoryURL, verificationDir, []string{})
|
||||
repositoryHelper.AssertReadmeContains(content)
|
||||
|
||||
forgeTree.Trace("======= NodeRepositoryDriverMirrorRef from a forgejo project to a directory")
|
||||
|
||||
masterRef := "refs/heads/master"
|
||||
forgejoRef := "refs/forgejo/test"
|
||||
directoryRef := "refs/directory/test"
|
||||
|
||||
log.Reset()
|
||||
helpers_repository.GitMirrorRef(ctx, log, repositoryURL, masterRef, destination, directoryRef)
|
||||
require.Contains(t, log.String(), "new branch")
|
||||
directorySha := helpers_repository.GitGetSha(ctx, log, destination, directoryRef)
|
||||
|
||||
forgeTree.Trace("======= NodeRepositoryDriverMirrorRef from one forgejo project to another forgejo project")
|
||||
|
||||
log.Reset()
|
||||
otherForgejoRef := "refs/otherforgejo/test"
|
||||
otherDirectoryRef := "refs/otherdirectory/test"
|
||||
helpers_repository.GitMirrorRef(ctx, log, repositoryURL, masterRef, otherRepositoryPushURL, otherForgejoRef)
|
||||
helpers_repository.GitMirrorRef(ctx, log, otherRepositoryURL, otherForgejoRef, destination, otherDirectoryRef)
|
||||
assert.EqualValues(t, directorySha, helpers_repository.GitGetSha(ctx, log, destination, otherDirectoryRef))
|
||||
|
||||
forgeTree.Trace("======= NodeRepositoryDriverMirrorRef from a directory to a forgejo project")
|
||||
|
||||
log.Reset()
|
||||
helpers_repository.GitMirrorRef(ctx, log, verificationDir, masterRef, otherRepositoryPushURL, forgejoRef)
|
||||
masterSha := helpers_repository.GitGetSha(ctx, log, verificationDir, masterRef)
|
||||
helpers_repository.GitMirrorRef(ctx, log, otherRepositoryURL, forgejoRef, verificationDir, directoryRef)
|
||||
assert.EqualValues(t, masterSha, helpers_repository.GitGetSha(ctx, log, verificationDir, directoryRef))
|
||||
}
|
19
tree/tests/f3/init.go
Normal file
19
tree/tests/f3/init.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package f3
|
||||
|
||||
import (
|
||||
// register filesystem test factory
|
||||
_ "code.forgejo.org/f3/gof3/v3/forges/filesystem"
|
||||
_ "code.forgejo.org/f3/gof3/v3/forges/filesystem/tests"
|
||||
|
||||
// register forgejo test factory
|
||||
_ "code.forgejo.org/f3/gof3/v3/forges/forgejo"
|
||||
_ "code.forgejo.org/f3/gof3/v3/forges/forgejo/tests"
|
||||
|
||||
// register gitlab test factory
|
||||
_ "code.forgejo.org/f3/gof3/v3/forges/gitlab"
|
||||
_ "code.forgejo.org/f3/gof3/v3/forges/gitlab/tests"
|
||||
)
|
16
tree/tests/f3/interface.go
Normal file
16
tree/tests/f3/interface.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package f3
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type TestingT interface {
|
||||
assert.TestingT
|
||||
TempDir() string
|
||||
Skip(args ...any)
|
||||
FailNow()
|
||||
}
|
9
tree/tests/f3/main_test.go
Normal file
9
tree/tests/f3/main_test.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package f3
|
||||
|
||||
import (
|
||||
_ "code.forgejo.org/f3/gof3/v3/forges"
|
||||
)
|
71
tree/tests/f3/pullrequest_test.go
Normal file
71
tree/tests/f3/pullrequest_test.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package f3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
filesystem_options "code.forgejo.org/f3/gof3/v3/forges/filesystem/options"
|
||||
"code.forgejo.org/f3/gof3/v3/options"
|
||||
"code.forgejo.org/f3/gof3/v3/path"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||
tests_forge "code.forgejo.org/f3/gof3/v3/tree/tests/f3/forge"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestF3PullRequest(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, factory := range tests_forge.GetFactories() {
|
||||
testForge := factory()
|
||||
t.Run(testForge.GetName(), func(t *testing.T) {
|
||||
// testCase.options will t.Skip if the forge instance is not up
|
||||
forgeWriteOptions := testForge.NewOptions(t)
|
||||
forgeReadOptions := testForge.NewOptions(t)
|
||||
forgeReadOptions.(options.URLInterface).SetURL(forgeWriteOptions.(options.URLInterface).GetURL())
|
||||
|
||||
fixtureTree := generic.GetFactory("f3")(ctx, tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t))
|
||||
fixtureTree.Trace("======= build fixture")
|
||||
TreeBuildPartial(t, "F3PullRequest"+testForge.GetName(), testForge.GetKindExceptions(), forgeWriteOptions, fixtureTree)
|
||||
|
||||
// craft a PR condition depending on testCase
|
||||
|
||||
fixtureTree.Trace("======= mirror fixture to forge")
|
||||
|
||||
forgeWriteTree := generic.GetFactory("f3")(ctx, forgeWriteOptions)
|
||||
generic.TreeMirror(ctx, fixtureTree, forgeWriteTree, generic.NewPathFromString(""), generic.NewMirrorOptions())
|
||||
|
||||
paths := []string{"/forge/users/10111/projects/74823/repositories", "/forge/users/10111/projects/74823/pull_requests"}
|
||||
pathPairs := make([][2]path.Path, 0, 5)
|
||||
for _, p := range paths {
|
||||
p := generic.NewPathFromString(p)
|
||||
pathPairs = append(pathPairs, [2]path.Path{p, generic.TreePathRemap(ctx, fixtureTree, p, forgeWriteTree)})
|
||||
}
|
||||
|
||||
fixtureTree.Trace("======= read from forge")
|
||||
|
||||
forgeReadTree := generic.GetFactory("f3")(ctx, forgeReadOptions)
|
||||
forgeReadTree.WalkAndGet(ctx, generic.NewWalkOptions(nil))
|
||||
|
||||
fixtureTree.Trace("======= mirror forge to filesystem")
|
||||
|
||||
verificationTree := generic.GetFactory("f3")(ctx, tests_forge.GetFactory(filesystem_options.Name)().NewOptions(t))
|
||||
|
||||
for _, pathPair := range pathPairs {
|
||||
generic.TreeMirror(ctx, forgeReadTree, verificationTree, pathPair[1], generic.NewMirrorOptions())
|
||||
}
|
||||
|
||||
fixtureTree.Trace("======= compare fixture with forge mirrored to filesystem")
|
||||
for _, pathPair := range pathPairs {
|
||||
fixtureTree.Trace("======= compare %s with %s", pathPair[0], pathPair[1])
|
||||
assert.True(t, generic.TreeCompare(ctx, fixtureTree, pathPair[0], verificationTree, pathPair[1]))
|
||||
}
|
||||
|
||||
TreeDelete(t, testForge.GetNonTestUsers(), forgeWriteOptions, forgeWriteTree)
|
||||
})
|
||||
}
|
||||
}
|
74
tree/tests/generic/compare_test.go
Normal file
74
tree/tests/generic/compare_test.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"code.forgejo.org/f3/gof3/v3/id"
|
||||
"code.forgejo.org/f3/gof3/v3/kind"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/memory"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCompare(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
aTree := NewMemoryTree(ctx, "O")
|
||||
testTreeBuild(t, aTree, 2)
|
||||
bTree := NewMemoryTree(ctx, "O")
|
||||
testTreeBuild(t, bTree, 2)
|
||||
|
||||
assert.True(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
|
||||
|
||||
{
|
||||
toDelete := generic.NewPathFromString("/O-A/O-B")
|
||||
|
||||
aTree.Find(toDelete).Delete(ctx)
|
||||
assert.False(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
|
||||
|
||||
bTree.Find(toDelete).Delete(ctx)
|
||||
assert.True(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
|
||||
}
|
||||
|
||||
{
|
||||
content := "OTHER CONTENT"
|
||||
toModify := generic.NewPathFromString("/O-A/O-F")
|
||||
aNode := aTree.Find(toModify)
|
||||
memory.SetContent(aNode, content)
|
||||
aNode.Upsert(ctx)
|
||||
assert.False(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
|
||||
|
||||
bNode := bTree.Find(toModify)
|
||||
memory.SetContent(bNode, content)
|
||||
bNode.Upsert(ctx)
|
||||
|
||||
assert.True(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
|
||||
}
|
||||
|
||||
{
|
||||
toModify := generic.NewPathFromString("/O-A/O-F")
|
||||
aTree.Find(toModify).SetKind(kind.Kind("???"))
|
||||
assert.False(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
|
||||
bTree.Find(toModify).SetKind(kind.Kind("???"))
|
||||
assert.True(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
|
||||
}
|
||||
|
||||
{
|
||||
pathToMap := generic.NewPathFromString("/O-A/O-J/O-M")
|
||||
mappedID := id.NewNodeID("MAPPED")
|
||||
aTree.Find(pathToMap).SetMappedID(mappedID)
|
||||
assert.False(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
|
||||
|
||||
bNode := bTree.Find(pathToMap).Delete(ctx)
|
||||
bNode.SetID(mappedID)
|
||||
parentPathToMap := generic.NewPathFromString("/O-A/O-J")
|
||||
bTree.Find(parentPathToMap).SetChild(bNode)
|
||||
assert.True(t, generic.TreeCompare(ctx, aTree, generic.NewPathFromString(""), bTree, generic.NewPathFromString("")))
|
||||
}
|
||||
}
|
193
tree/tests/generic/memory_test.go
Normal file
193
tree/tests/generic/memory_test.go
Normal file
|
@ -0,0 +1,193 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"code.forgejo.org/f3/gof3/v3/id"
|
||||
"code.forgejo.org/f3/gof3/v3/kind"
|
||||
"code.forgejo.org/f3/gof3/v3/path"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/memory"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func NewMemoryTree(ctx context.Context, name string) generic.TreeInterface {
|
||||
return generic.GetFactory("memory")(ctx, memory.NewOptions(memory.NewIDAllocatorGenerator(name)))
|
||||
}
|
||||
|
||||
func TestMemoryTreeIDAllocator(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
name := "T"
|
||||
id := "THEID"
|
||||
|
||||
for _, testCase := range []struct {
|
||||
idAllocator memory.IDAllocatorInterface
|
||||
setID bool
|
||||
expectedID string
|
||||
}{
|
||||
{
|
||||
idAllocator: memory.NewIDAllocatorGenerator(name),
|
||||
setID: false,
|
||||
expectedID: name + "-A",
|
||||
},
|
||||
{
|
||||
idAllocator: memory.NewIDAllocatorNull(),
|
||||
setID: true,
|
||||
expectedID: id,
|
||||
},
|
||||
} {
|
||||
t.Run(testCase.expectedID, func(t *testing.T) {
|
||||
tree := generic.GetFactory("memory")(ctx, memory.NewOptions(testCase.idAllocator))
|
||||
node := tree.GetRoot().CreateChild(ctx)
|
||||
f := memory.NewFormat("")
|
||||
if testCase.setID {
|
||||
f.SetID(id)
|
||||
}
|
||||
node.FromFormat(f)
|
||||
node.Upsert(ctx)
|
||||
assert.EqualValues(t, testCase.expectedID, node.GetID())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testTreeBuild(t *testing.T, tree generic.TreeInterface, maxDepth int) {
|
||||
ctx := context.Background()
|
||||
|
||||
insert := func(tree generic.TreeInterface, parent generic.NodeInterface) generic.NodeInterface {
|
||||
node := parent.CreateChild(ctx)
|
||||
node.Upsert(ctx)
|
||||
memory.SetContent(node, "content "+node.GetID().String())
|
||||
node.Upsert(ctx)
|
||||
return node
|
||||
}
|
||||
|
||||
var populate func(depth int, tree generic.TreeInterface, parent generic.NodeInterface)
|
||||
populate = func(depth int, tree generic.TreeInterface, parent generic.NodeInterface) {
|
||||
if depth >= maxDepth {
|
||||
return
|
||||
}
|
||||
depth++
|
||||
for i := 1; i <= 3; i++ {
|
||||
node := insert(tree, parent)
|
||||
populate(depth, tree, node)
|
||||
}
|
||||
}
|
||||
|
||||
populate(0, tree, insert(tree, tree.GetRoot()))
|
||||
}
|
||||
|
||||
func TestMemoryTreeBuild(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
verify := func(tree generic.TreeInterface, expected []string) {
|
||||
collected := make([]string, 0, 10)
|
||||
collect := func(ctx context.Context, p path.Path, node generic.NodeInterface) {
|
||||
if node.GetKind() == kind.KindRoot {
|
||||
return
|
||||
}
|
||||
p = p.Append(node)
|
||||
collected = append(collected, p.String()+":"+memory.GetContent(node))
|
||||
}
|
||||
tree.WalkAndGet(ctx, generic.NewWalkOptions(collect))
|
||||
sort.Strings(expected)
|
||||
sort.Strings(collected)
|
||||
assert.EqualValues(t, expected, collected)
|
||||
}
|
||||
|
||||
for _, testCase := range []struct {
|
||||
name string
|
||||
build func(tree generic.TreeInterface)
|
||||
operations func(tree generic.TreeInterface)
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "full tree",
|
||||
build: func(tree generic.TreeInterface) { testTreeBuild(t, tree, 2) },
|
||||
operations: func(tree generic.TreeInterface) {},
|
||||
expected: []string{"/T-A/T-B/T-C:content T-C", "/T-A/T-B/T-D:content T-D", "/T-A/T-B/T-E:content T-E", "/T-A/T-B:content T-B", "/T-A/T-F/T-G:content T-G", "/T-A/T-F/T-H:content T-H", "/T-A/T-F/T-I:content T-I", "/T-A/T-F:content T-F", "/T-A/T-J/T-K:content T-K", "/T-A/T-J/T-L:content T-L", "/T-A/T-J/T-M:content T-M", "/T-A/T-J:content T-J", "/T-A:content T-A"},
|
||||
},
|
||||
{
|
||||
name: "scenario 1",
|
||||
build: func(tree generic.TreeInterface) { testTreeBuild(t, tree, 2) },
|
||||
operations: func(tree generic.TreeInterface) {
|
||||
root := tree.GetRoot()
|
||||
|
||||
id0 := id.NewNodeID("T-A")
|
||||
root.List(ctx)
|
||||
zero := root.GetChild(id0)
|
||||
assert.False(t, generic.NilNode == zero)
|
||||
assert.True(t, zero == zero.Get(ctx))
|
||||
|
||||
id1 := id.NewNodeID("T-B")
|
||||
zero.List(ctx)
|
||||
one := zero.GetChild(id1)
|
||||
assert.False(t, generic.NilNode == one)
|
||||
one.Get(ctx)
|
||||
memory.SetContent(one, "other one")
|
||||
one.Upsert(ctx)
|
||||
|
||||
id2 := id.NewNodeID("T-F")
|
||||
two := zero.GetChild(id2)
|
||||
two.Delete(ctx)
|
||||
two.Delete(ctx)
|
||||
assert.True(t, generic.NilNode == zero.GetChild(id2))
|
||||
},
|
||||
expected: []string{"/T-A/T-B/T-C:content T-C", "/T-A/T-B/T-D:content T-D", "/T-A/T-B/T-E:content T-E", "/T-A/T-B:other one", "/T-A/T-J/T-K:content T-K", "/T-A/T-J/T-L:content T-L", "/T-A/T-J/T-M:content T-M", "/T-A/T-J:content T-J", "/T-A:content T-A"},
|
||||
},
|
||||
{
|
||||
name: "scenario 2",
|
||||
build: func(tree generic.TreeInterface) { testTreeBuild(t, tree, 0) },
|
||||
operations: func(tree generic.TreeInterface) {
|
||||
root := tree.GetRoot()
|
||||
|
||||
id0 := id.NewNodeID("T-A")
|
||||
root.List(ctx)
|
||||
zero := root.GetChild(id0)
|
||||
assert.False(t, generic.NilNode == zero)
|
||||
zero.Get(ctx)
|
||||
|
||||
one := zero.CreateChild(ctx)
|
||||
one.Upsert(ctx)
|
||||
memory.SetContent(one, "ONE")
|
||||
one.Upsert(ctx)
|
||||
|
||||
two := one.CreateChild(ctx)
|
||||
two.Upsert(ctx)
|
||||
memory.SetContent(two, "SOMETHING")
|
||||
two.Upsert(ctx)
|
||||
memory.SetContent(two, "ONE/TWO")
|
||||
two.Upsert(ctx)
|
||||
one.DeleteChild(two.GetID())
|
||||
two.Get(ctx)
|
||||
|
||||
three := two.CreateChild(ctx)
|
||||
three.Upsert(ctx)
|
||||
memory.SetContent(three, "ONE/THREE")
|
||||
three.Upsert(ctx)
|
||||
three.Delete(ctx)
|
||||
},
|
||||
expected: []string{"/T-A/T-B/T-C:ONE/TWO", "/T-A/T-B:ONE", "/T-A:content T-A"},
|
||||
},
|
||||
} {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
tree := NewMemoryTree(ctx, "T")
|
||||
|
||||
tree.Trace("========== BUILD")
|
||||
testCase.build(tree)
|
||||
tree.Trace("========== OPERATIONS")
|
||||
testCase.operations(tree)
|
||||
verify(tree, testCase.expected)
|
||||
tree.Trace("========== VERIFY RELOAD")
|
||||
tree.Clear(ctx)
|
||||
verify(tree, testCase.expected)
|
||||
})
|
||||
}
|
||||
}
|
98
tree/tests/generic/mirror_test.go
Normal file
98
tree/tests/generic/mirror_test.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"code.forgejo.org/f3/gof3/v3/path"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/memory"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type testReference struct {
|
||||
originPath string
|
||||
originReference string
|
||||
destinationPath string
|
||||
destinationReference string
|
||||
}
|
||||
|
||||
func TestMirror(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, testCase := range []struct {
|
||||
start string
|
||||
references []testReference
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
start: "/",
|
||||
expected: []string{":ROOT:", "/D-A:content O-A:", "/D-A/D-B:content O-B:", "/D-A/D-B/D-C:content O-C:", "/D-A/D-B/D-D:content O-D:", "/D-A/D-B/D-E:content O-E:", "/D-A/D-F:content O-F:", "/D-A/D-F/D-G:content O-G:", "/D-A/D-F/D-H:content O-H:", "/D-A/D-F/D-I:content O-I:", "/D-A/D-J:content O-J:", "/D-A/D-J/D-K:content O-K:", "/D-A/D-J/D-L:content O-L:", "/D-A/D-J/D-M:content O-M:"},
|
||||
},
|
||||
{
|
||||
start: "/O-A/O-B",
|
||||
references: []testReference{
|
||||
{
|
||||
originPath: "/O-A/O-B/O-C",
|
||||
originReference: "/O-A/O-F",
|
||||
destinationPath: "/D-A/D-B/D-D",
|
||||
destinationReference: "/D-A/D-C",
|
||||
},
|
||||
},
|
||||
expected: []string{":ROOT:", "/D-A:content O-A:", "/D-A/D-B:content O-B:", "/D-A/D-B/D-D:content O-C:/D-A/D-C", "/D-A/D-B/D-E:content O-D:", "/D-A/D-B/D-F:content O-E:", "/D-A/D-C:content O-F:"},
|
||||
},
|
||||
{
|
||||
start: "/O-A/O-F",
|
||||
references: []testReference{
|
||||
{
|
||||
originPath: "/O-A/O-F/O-G",
|
||||
originReference: "../../O-J",
|
||||
destinationPath: "/D-A/D-B/D-D",
|
||||
destinationReference: "../../D-C",
|
||||
},
|
||||
},
|
||||
expected: []string{":ROOT:", "/D-A:content O-A:", "/D-A/D-B:content O-F:", "/D-A/D-B/D-D:content O-G:../../D-C", "/D-A/D-B/D-E:content O-H:", "/D-A/D-B/D-F:content O-I:", "/D-A/D-C:content O-J:"},
|
||||
},
|
||||
} {
|
||||
t.Run(" "+testCase.start, func(t *testing.T) {
|
||||
originTree := NewMemoryTree(ctx, "O")
|
||||
log := originTree.GetLogger()
|
||||
log.Trace("=========== build")
|
||||
testTreeBuild(t, originTree, 2)
|
||||
|
||||
for _, c := range testCase.references {
|
||||
log.Trace("=========== inject reference %s", c.originReference)
|
||||
assert.True(t, originTree.Apply(ctx, generic.NewPathFromString(c.originPath), generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
|
||||
memory.SetRef(node, c.originReference)
|
||||
})))
|
||||
}
|
||||
|
||||
log.Trace("=========== mirror")
|
||||
destinationTree := NewMemoryTree(ctx, "D")
|
||||
|
||||
generic.TreeMirror(ctx, originTree, destinationTree, generic.NewPathFromString(testCase.start), generic.NewMirrorOptions())
|
||||
log.Trace("=========== verify")
|
||||
collected := make([]string, 0, 10)
|
||||
collect := func(ctx context.Context, parent path.Path, node generic.NodeInterface) {
|
||||
collected = append(collected, node.GetCurrentPath().String()+":"+memory.GetContent(node)+":"+memory.GetRef(node))
|
||||
}
|
||||
destinationTree.Walk(ctx, generic.NewWalkOptions(collect))
|
||||
assert.EqualValues(t, testCase.expected, collected)
|
||||
|
||||
for _, c := range testCase.references {
|
||||
log.Trace("=========== look for reference %s", c.destinationReference)
|
||||
var called bool
|
||||
assert.True(t, destinationTree.Apply(ctx, generic.NewPathFromString(c.destinationPath), generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
|
||||
assert.EqualValues(t, c.destinationReference, memory.GetRef(node))
|
||||
called = true
|
||||
})))
|
||||
assert.True(t, called)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
341
tree/tests/generic/node_walk_test.go
Normal file
341
tree/tests/generic/node_walk_test.go
Normal file
|
@ -0,0 +1,341 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"code.forgejo.org/f3/gof3/v3/path"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/memory"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBothApplyWalk(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
tree := NewMemoryTree(ctx, "T")
|
||||
|
||||
for _, testCase := range []struct {
|
||||
path string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
path: "/T-A",
|
||||
expected: []string{"/T-A", "/T-A/T-B", "/T-A/T-B/T-C", "/T-A/T-B/T-D", "/T-A/T-B/T-E", "/T-A/T-F", "/T-A/T-F/T-G", "/T-A/T-F/T-H", "/T-A/T-F/T-I", "/T-A/T-J", "/T-A/T-J/T-K", "/T-A/T-J/T-L", "/T-A/T-J/T-M"},
|
||||
},
|
||||
{
|
||||
path: "/T-A/T-B",
|
||||
expected: []string{"/T-A/T-B", "/T-A/T-B/T-C", "/T-A/T-B/T-D", "/T-A/T-B/T-E"},
|
||||
},
|
||||
{
|
||||
path: "/T-A/T-B/T-C",
|
||||
expected: []string{"/T-A/T-B/T-C"},
|
||||
},
|
||||
} {
|
||||
testTreeBuild(t, tree, 2)
|
||||
|
||||
collected := make([]string, 0, 10)
|
||||
p := generic.NewPathFromString(testCase.path)
|
||||
walk := func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
|
||||
collect := func(ctx context.Context, p path.Path, node generic.NodeInterface) {
|
||||
p = p.Append(node)
|
||||
collected = append(collected, p.String())
|
||||
}
|
||||
node.Walk(ctx, parent, generic.NewWalkOptions(collect))
|
||||
}
|
||||
assert.True(t, tree.Apply(ctx, p, generic.NewApplyOptions(walk)))
|
||||
sort.Strings(testCase.expected)
|
||||
sort.Strings(collected)
|
||||
assert.EqualValues(t, testCase.expected, collected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalk(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
tree := NewMemoryTree(ctx, "T")
|
||||
|
||||
expected := []string{"", "/T-A", "/T-A/T-B", "/T-A/T-B/T-C", "/T-A/T-B/T-D", "/T-A/T-B/T-E", "/T-A/T-F", "/T-A/T-F/T-G", "/T-A/T-F/T-H", "/T-A/T-F/T-I", "/T-A/T-J", "/T-A/T-J/T-K", "/T-A/T-J/T-L", "/T-A/T-J/T-M"}
|
||||
testTreeBuild(t, tree, 2)
|
||||
|
||||
collected := make([]string, 0, 10)
|
||||
collect := func(ctx context.Context, p path.Path, node generic.NodeInterface) {
|
||||
p = p.Append(node)
|
||||
collected = append(collected, p.String())
|
||||
}
|
||||
tree.Walk(ctx, generic.NewWalkOptions(collect))
|
||||
sort.Strings(expected)
|
||||
sort.Strings(collected)
|
||||
assert.EqualValues(t, expected, collected)
|
||||
}
|
||||
|
||||
func TestWalkAndGet(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
tree := NewMemoryTree(ctx, "T")
|
||||
|
||||
testTreeBuild(t, tree, 1)
|
||||
tree.Clear(ctx)
|
||||
|
||||
collected := make([]string, 0, 10)
|
||||
collect := func(ctx context.Context, p path.Path, node generic.NodeInterface) {
|
||||
p = p.Append(node)
|
||||
collected = append(collected, p.String())
|
||||
}
|
||||
tree.Walk(ctx, generic.NewWalkOptions(collect))
|
||||
assert.EqualValues(t, []string{""}, collected)
|
||||
|
||||
collected = make([]string, 0, 10)
|
||||
tree.WalkAndGet(ctx, generic.NewWalkOptions(collect))
|
||||
expected := []string{"", "/T-A", "/T-A/T-B", "/T-A/T-C", "/T-A/T-D"}
|
||||
sort.Strings(expected)
|
||||
sort.Strings(collected)
|
||||
assert.EqualValues(t, expected, collected)
|
||||
}
|
||||
|
||||
func TestApplyVisitID(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
tree := NewMemoryTree(ctx, "T")
|
||||
testTreeBuild(t, tree, 2)
|
||||
|
||||
for _, testPath := range []string{"/T-A", "/T-A/T-B", "/T-A/T-B/T-C"} {
|
||||
|
||||
collected := make([]string, 0, 10)
|
||||
p := generic.NewPathFromString(testPath)
|
||||
collect := func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
|
||||
parent = parent.Append(node)
|
||||
assert.False(t, node.GetIsNil(), node.String())
|
||||
assert.EqualValues(t, parent.Length(), node.GetCurrentPath().Length())
|
||||
expected := parent.PathString().Join()
|
||||
actual := node.GetCurrentPath().String()
|
||||
assert.EqualValues(t, actual, expected)
|
||||
collected = append(collected, parent.String())
|
||||
}
|
||||
assert.True(t, tree.Apply(ctx, p, generic.NewApplyOptions(collect)))
|
||||
if assert.EqualValues(t, 1, len(collected)) {
|
||||
assert.EqualValues(t, testPath, collected[0])
|
||||
}
|
||||
}
|
||||
|
||||
p := generic.NewPathFromString("/1/2/3/4")
|
||||
called := false
|
||||
assert.False(t, tree.Apply(ctx, p, generic.NewApplyOptions(func(context.Context, path.Path, path.Path, generic.NodeInterface) { called = true })))
|
||||
assert.False(t, called)
|
||||
}
|
||||
|
||||
func TestApplyVisitByName(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
tree := NewMemoryTree(ctx, "T")
|
||||
testTreeBuild(t, tree, 2)
|
||||
|
||||
for _, testCase := range []struct {
|
||||
path string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
path: "/T-A/content T-B/T-C",
|
||||
expected: "/T-A/T-B/T-C",
|
||||
},
|
||||
{
|
||||
path: "/T-A/content T-B/content T-C",
|
||||
expected: "/T-A/T-B/T-C",
|
||||
},
|
||||
{
|
||||
path: "/content T-A/content T-B/content T-C",
|
||||
expected: "/T-A/T-B/T-C",
|
||||
},
|
||||
} {
|
||||
collected := make([]string, 0, 10)
|
||||
p := generic.NewPathFromString(testCase.path)
|
||||
collect := func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
|
||||
parent = parent.Append(node)
|
||||
assert.False(t, node.GetIsNil(), node.String())
|
||||
assert.EqualValues(t, parent.Length(), node.GetCurrentPath().Length())
|
||||
expected := parent.PathString().Join()
|
||||
actual := node.GetCurrentPath().String()
|
||||
assert.EqualValues(t, actual, expected)
|
||||
collected = append(collected, parent.String())
|
||||
}
|
||||
assert.True(t, tree.Apply(ctx, p, generic.NewApplyOptions(collect).SetSearch(generic.ApplySearchByName)))
|
||||
if assert.EqualValues(t, 1, len(collected)) {
|
||||
assert.EqualValues(t, testCase.expected, collected[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyAndGet(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, testCase := range []struct {
|
||||
path string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
path: "/T-A",
|
||||
expected: []string{"", "/T-A"},
|
||||
},
|
||||
{
|
||||
path: "/T-A/T-B",
|
||||
expected: []string{"", "/T-A", "/T-A/T-B"},
|
||||
},
|
||||
{
|
||||
path: "/T-A/T-B/T-C",
|
||||
expected: []string{"", "/T-A", "/T-A/T-B", "/T-A/T-B/T-C"},
|
||||
},
|
||||
} {
|
||||
tree := NewMemoryTree(ctx, "T")
|
||||
|
||||
testTreeBuild(t, tree, 2)
|
||||
tree.Clear(ctx)
|
||||
|
||||
p := generic.NewPathFromString(testCase.path)
|
||||
|
||||
var collected []string
|
||||
collect := func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
|
||||
parent = parent.Append(node)
|
||||
collected = append(collected, parent.String())
|
||||
}
|
||||
|
||||
{
|
||||
collected = make([]string, 0, 10)
|
||||
require.False(t, tree.Apply(ctx, p, generic.NewApplyOptions(collect)))
|
||||
}
|
||||
|
||||
{
|
||||
collected = make([]string, 0, 10)
|
||||
require.True(t, tree.ApplyAndGet(ctx, p, generic.NewApplyOptions(collect)))
|
||||
require.EqualValues(t, 1, len(collected))
|
||||
assert.EqualValues(t, testCase.path, collected[0])
|
||||
}
|
||||
|
||||
{
|
||||
collected = make([]string, 0, 10)
|
||||
require.True(t, tree.ApplyAndGet(ctx, p, generic.NewApplyOptions(collect).SetWhere(generic.ApplyEachNode)))
|
||||
sort.Strings(testCase.expected)
|
||||
sort.Strings(collected)
|
||||
assert.EqualValues(t, testCase.expected, collected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyVisitRelative(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
tree := NewMemoryTree(ctx, "T")
|
||||
|
||||
// The "destination" is clean and there is no need to test a/../b which becomes
|
||||
// b etc. Only when the first element of the path is either an id or ..
|
||||
for _, testCase := range []struct {
|
||||
start string
|
||||
destination string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
start: "/T-A",
|
||||
destination: "T-B",
|
||||
expected: "/T-A/T-B",
|
||||
},
|
||||
{
|
||||
start: "/T-A/T-B",
|
||||
destination: ".",
|
||||
expected: "/T-A/T-B",
|
||||
},
|
||||
{
|
||||
start: "/T-A/T-B",
|
||||
destination: "T-C",
|
||||
expected: "/T-A/T-B/T-C",
|
||||
},
|
||||
{
|
||||
start: "/T-A/T-B",
|
||||
destination: "..",
|
||||
expected: "/T-A",
|
||||
},
|
||||
{
|
||||
start: "/T-A/T-B",
|
||||
destination: "../T-F/T-G",
|
||||
expected: "/T-A/T-F/T-G",
|
||||
},
|
||||
{
|
||||
start: "/T-A/T-B/T-C",
|
||||
destination: "../../T-F",
|
||||
expected: "/T-A/T-F",
|
||||
},
|
||||
} {
|
||||
t.Run(" "+testCase.start+" => "+testCase.destination, func(t *testing.T) {
|
||||
testTreeBuild(t, tree, 2)
|
||||
|
||||
collected := make([]string, 0, 10)
|
||||
start := generic.NewPathFromString(testCase.start)
|
||||
collect := func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
|
||||
parent = parent.Append(node)
|
||||
assert.False(t, node.GetIsNil(), node.String())
|
||||
assert.EqualValues(t, parent.Length(), node.GetCurrentPath().Length())
|
||||
expected := parent.PathString().Join()
|
||||
actual := node.GetCurrentPath().String()
|
||||
assert.EqualValues(t, actual, expected)
|
||||
collected = append(collected, parent.String())
|
||||
}
|
||||
cd := func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
|
||||
destination := generic.NewPathFromString(testCase.destination)
|
||||
fmt.Println("start ", node.GetCurrentPath().String())
|
||||
assert.True(t, node.Apply(ctx, parent, destination, generic.NewApplyOptions(collect)))
|
||||
}
|
||||
assert.True(t, tree.Apply(ctx, start, generic.NewApplyOptions(cd)))
|
||||
if assert.EqualValues(t, 1, len(collected)) {
|
||||
assert.EqualValues(t, testCase.expected, collected[0])
|
||||
}
|
||||
})
|
||||
|
||||
p := generic.NewPathFromString("/1/2/3/4")
|
||||
called := false
|
||||
assert.False(t, tree.Apply(ctx, p, generic.NewApplyOptions(func(context.Context, path.Path, path.Path, generic.NodeInterface) { called = true })))
|
||||
assert.False(t, called)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyUpsert(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
tree := NewMemoryTree(ctx, "T")
|
||||
|
||||
testTreeBuild(t, tree, 2)
|
||||
|
||||
expected := generic.NewPathFromString("/T-A/T-B/T-N")
|
||||
assert.False(t, tree.Exists(ctx, expected))
|
||||
|
||||
assert.True(t, tree.Apply(ctx, generic.NewPathFromString("/T-A/T-B"), generic.NewApplyOptions(func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
|
||||
new := node.CreateChild(ctx)
|
||||
new.Upsert(ctx)
|
||||
memory.SetContent(new, "content "+new.GetID().String())
|
||||
new.Upsert(ctx)
|
||||
})))
|
||||
|
||||
assert.True(t, tree.Exists(ctx, expected))
|
||||
}
|
||||
|
||||
func TestApplyDelete(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
tree := NewMemoryTree(ctx, "T")
|
||||
|
||||
testTreeBuild(t, tree, 2)
|
||||
|
||||
toDelete := generic.NewPathFromString("/T-A/T-B")
|
||||
assert.True(t, tree.Exists(ctx, toDelete))
|
||||
|
||||
assert.True(t, tree.Apply(ctx, toDelete, generic.NewApplyOptions(func(ctx context.Context, parent, p path.Path, node generic.NodeInterface) {
|
||||
node.Delete(ctx)
|
||||
})))
|
||||
|
||||
assert.False(t, tree.Exists(ctx, toDelete))
|
||||
}
|
74
tree/tests/generic/references_test.go
Normal file
74
tree/tests/generic/references_test.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"code.forgejo.org/f3/gof3/v3/path"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/memory"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestTreeCollectReferences(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, testCase := range []struct {
|
||||
path string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
path: "/O-A/O-B",
|
||||
expected: []string{"/O-A/O-B/O-C"},
|
||||
},
|
||||
{
|
||||
path: "/O-A/O-D",
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
path: "/O-A/O-J",
|
||||
expected: []string{"/O-A/O-F", "/O-A/O-J/O-K"},
|
||||
},
|
||||
{
|
||||
path: "/O-A/O-J/O-L",
|
||||
expected: []string{"/O-A/O-J/O-K"},
|
||||
},
|
||||
{
|
||||
path: "/O-A/O-J/O-M",
|
||||
expected: []string{"/O-A/O-F"},
|
||||
},
|
||||
{
|
||||
path: "/O-A",
|
||||
expected: []string{"/O-A/O-B/O-C", "/O-A/O-F", "/O-A/O-F/O-H", "/O-A/O-J/O-K"},
|
||||
},
|
||||
} {
|
||||
t.Run(testCase.path, func(t *testing.T) {
|
||||
tree := NewMemoryTree(ctx, "O")
|
||||
testTreeBuild(t, tree, 2)
|
||||
|
||||
setReference := func(p, reference string) {
|
||||
assert.True(t, tree.Apply(ctx, generic.NewPathFromString(p), generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
|
||||
memory.SetRef(node, reference)
|
||||
})))
|
||||
}
|
||||
setReference("/O-A/O-B", "/O-A/O-B/O-C")
|
||||
setReference("/O-A/O-F/O-G", "/O-A/O-F/O-H")
|
||||
setReference("/O-A/O-J/O-M", "../../O-F")
|
||||
setReference("/O-A/O-J/O-L", "../O-K")
|
||||
|
||||
actual := make([]string, 0, 10)
|
||||
for _, reference := range generic.TreeCollectReferences(ctx, tree, generic.NewPathFromString(testCase.path)) {
|
||||
actual = append(actual, reference.String())
|
||||
}
|
||||
sort.Strings(actual)
|
||||
|
||||
assert.EqualValues(t, testCase.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
626
tree/tests/generic/unify_test.go
Normal file
626
tree/tests/generic/unify_test.go
Normal file
|
@ -0,0 +1,626 @@
|
|||
// Copyright Earl Warren <contact@earl-warren.org>
|
||||
// Copyright Loïc Dachary <loic@dachary.org>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package generic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"code.forgejo.org/f3/gof3/v3/kind"
|
||||
"code.forgejo.org/f3/gof3/v3/path"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/generic"
|
||||
"code.forgejo.org/f3/gof3/v3/tree/memory"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestUnifyPathSimpleRemap(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, testCase := range []struct {
|
||||
path string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
path: "",
|
||||
expected: []string{},
|
||||
},
|
||||
{
|
||||
path: "/O-A",
|
||||
expected: []string{"/O-A:O-A=content O-A => /D-A:D-A=content O-A"},
|
||||
},
|
||||
{
|
||||
path: "/O-A/O-B",
|
||||
expected: []string{"/O-A:O-A=content O-A => /D-A:D-A=content O-A", "/O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B"},
|
||||
},
|
||||
{
|
||||
path: "/O-A/O-B/O-C",
|
||||
expected: []string{"/O-A:O-A=content O-A => /D-A:D-A=content O-A", "/O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "/O-A/O-B/O-C:O-C=content O-C => /D-A/D-B/D-C:D-C=content O-C"},
|
||||
},
|
||||
} {
|
||||
t.Run(" "+testCase.path, func(t *testing.T) {
|
||||
originTree := NewMemoryTree(ctx, "O")
|
||||
testTreeBuild(t, originTree, 2)
|
||||
destinationTree := NewMemoryTree(ctx, "D")
|
||||
|
||||
collected := make([]string, 0, 10)
|
||||
p := generic.NewPathFromString(testCase.path)
|
||||
upsert := func(ctx context.Context, origin generic.NodeInterface, originPath path.Path, destination generic.NodeInterface, destinationPath path.Path) {
|
||||
fmt.Printf("origin %v destination %v\n", origin, destination)
|
||||
originPath = originPath.Append(origin)
|
||||
destinationPath = destinationPath.Append(destination)
|
||||
collected = append(collected, originPath.String()+":"+origin.String()+" => "+destinationPath.String()+":"+destination.String())
|
||||
}
|
||||
generic.TreeUnifyPath(ctx, originTree, p, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
|
||||
assert.EqualValues(t, testCase.expected, collected)
|
||||
collected = make([]string, 0, 10)
|
||||
generic.TreeUnifyPath(ctx, originTree, p, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
|
||||
assert.EqualValues(t, testCase.expected, collected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnifyPathSimpleNoRemap(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, testCase := range []struct {
|
||||
noremap bool
|
||||
path string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
noremap: false,
|
||||
path: "/O-A/O-B",
|
||||
expected: []string{"/O-A:O-A=content O-A => /D-A:D-A=content O-A", "/O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B"},
|
||||
},
|
||||
{
|
||||
noremap: true,
|
||||
path: "/O-A/O-B",
|
||||
expected: []string{"/O-A:O-A=content O-A => /O-A:O-A=content O-A", "/O-A/O-B:O-B=content O-B => /O-A/O-B:O-B=content O-B"},
|
||||
},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("noremap=%v,path=%s", testCase.noremap, testCase.path), func(t *testing.T) {
|
||||
originName := "O"
|
||||
originTree := NewMemoryTree(ctx, originName)
|
||||
testTreeBuild(t, originTree, 2)
|
||||
var destinationName string
|
||||
if testCase.noremap {
|
||||
destinationName = originName
|
||||
} else {
|
||||
destinationName = "D"
|
||||
}
|
||||
destinationTree := NewMemoryTree(ctx, destinationName)
|
||||
|
||||
collected := make([]string, 0, 10)
|
||||
p := generic.NewPathFromString(testCase.path)
|
||||
upsert := func(ctx context.Context, origin generic.NodeInterface, originPath path.Path, destination generic.NodeInterface, destinationPath path.Path) {
|
||||
fmt.Printf("origin %v destination %v\n", origin, destination)
|
||||
originPath = originPath.Append(origin)
|
||||
destinationPath = destinationPath.Append(destination)
|
||||
collected = append(collected, originPath.String()+":"+origin.String()+" => "+destinationPath.String()+":"+destination.String())
|
||||
}
|
||||
generic.TreeUnifyPath(ctx, originTree, p, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert).SetNoRemap(testCase.noremap))
|
||||
assert.EqualValues(t, testCase.expected, collected)
|
||||
collected = make([]string, 0, 10)
|
||||
generic.TreeUnifyPath(ctx, originTree, p, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert).SetNoRemap(testCase.noremap))
|
||||
assert.EqualValues(t, testCase.expected, collected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnifyPathRelative(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, testCase := range []struct {
|
||||
start string
|
||||
destination string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
start: "/O-A/O-B",
|
||||
destination: "O-C",
|
||||
expected: []string{"cd: /O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "unify: /O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "unify: /O-A/O-B/O-C:O-C=content O-C => /D-A/D-B/D-C:D-C=content O-C"},
|
||||
},
|
||||
{
|
||||
start: "/O-A/O-B",
|
||||
destination: ".",
|
||||
expected: []string{"cd: /O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "unify: /O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B"},
|
||||
},
|
||||
{
|
||||
start: "/O-A/O-B",
|
||||
destination: "../O-F/O-G",
|
||||
expected: []string{"cd: /O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "unify: /O-A:O-A=content O-A => /D-A:D-A=content O-A", "unify: /O-A/O-F:O-F=content O-F => /D-A/D-C:D-C=content O-F", "unify: /O-A/O-F/O-G:O-G=content O-G => /D-A/D-C/D-D:D-D=content O-G"},
|
||||
},
|
||||
{
|
||||
start: "/O-A/O-B/O-C",
|
||||
destination: "../O-E",
|
||||
expected: []string{"cd: /O-A/O-B/O-C:O-C=content O-C => /D-A/D-B/D-C:D-C=content O-C", "unify: /O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "unify: /O-A/O-B/O-E:O-E=content O-E => /D-A/D-B/D-D:D-D=content O-E"},
|
||||
},
|
||||
} {
|
||||
t.Run(" "+testCase.start+" => "+testCase.destination, func(t *testing.T) {
|
||||
originTree := NewMemoryTree(ctx, "O")
|
||||
testTreeBuild(t, originTree, 2)
|
||||
destinationTree := NewMemoryTree(ctx, "D")
|
||||
|
||||
var collected []string
|
||||
start := generic.NewPathFromString(testCase.start)
|
||||
collect := func(prefix string, origin, destination generic.NodeInterface) {
|
||||
originPath := origin.GetCurrentPath().String()
|
||||
destinationPath := destination.GetCurrentPath().String()
|
||||
collected = append(collected, prefix+originPath+":"+origin.GetSelf().String()+" => "+destinationPath+":"+destination.GetSelf().String())
|
||||
}
|
||||
//
|
||||
// Unify testCase.start
|
||||
//
|
||||
upsert := func(ctx context.Context, origin generic.NodeInterface, originParent path.Path, destination generic.NodeInterface, destinationParent path.Path) {
|
||||
collect("unify: ", origin, destination)
|
||||
}
|
||||
generic.TreeUnifyPath(ctx, originTree, start, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
|
||||
//
|
||||
// Beginning from testCase.start, unify testCase.destination
|
||||
//
|
||||
cd := func(ctx context.Context, origin, destination generic.NodeInterface) {
|
||||
collect("cd: ", origin, destination)
|
||||
path := generic.NewPathFromString(testCase.destination)
|
||||
originParent := origin.GetParent().GetCurrentPath()
|
||||
destinationParent := destination.GetParent().GetCurrentPath()
|
||||
generic.NodeUnifyPath(ctx, origin.GetSelf(), originParent, path, destinationParent, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
|
||||
}
|
||||
//
|
||||
//
|
||||
//
|
||||
collected = make([]string, 0, 10)
|
||||
generic.TreeParallelApply(ctx, originTree, start, destinationTree, generic.NewParallelApplyOptions(cd))
|
||||
assert.EqualValues(t, testCase.expected, collected)
|
||||
//
|
||||
// Do it twice to verify it is idempotent
|
||||
//
|
||||
collected = make([]string, 0, 10)
|
||||
generic.TreeParallelApply(ctx, originTree, start, destinationTree, generic.NewParallelApplyOptions(cd))
|
||||
assert.EqualValues(t, testCase.expected, collected)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnifyPathScenario(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
//
|
||||
// build and populate a tree
|
||||
//
|
||||
originTree := NewMemoryTree(ctx, "O")
|
||||
testTreeBuild(t, originTree, 2)
|
||||
|
||||
//
|
||||
// build an empty tree
|
||||
//
|
||||
destinationTree := NewMemoryTree(ctx, "D")
|
||||
|
||||
//
|
||||
// accumulate the call results for verification
|
||||
//
|
||||
var collected []string
|
||||
upsert := func(ctx context.Context, origin generic.NodeInterface, originPath path.Path, destination generic.NodeInterface, destinationPath path.Path) {
|
||||
originPath = originPath.Append(origin)
|
||||
destinationPath = destinationPath.Append(destination)
|
||||
what := originPath.String() + ":" + origin.String() + " => " + destinationPath.String() + ":" + destination.String()
|
||||
fmt.Printf("unify: %T => %T | %s\n", origin, destination, what)
|
||||
collected = append(collected, what)
|
||||
}
|
||||
|
||||
assertTree := func(tree generic.TreeInterface, expected []string) {
|
||||
collected := make([]string, 0, 10)
|
||||
tree.Walk(ctx, generic.NewWalkOptions(func(ctx context.Context, path path.Path, node generic.NodeInterface) {
|
||||
if node.GetKind() == kind.KindRoot {
|
||||
return
|
||||
}
|
||||
path = path.Append(node)
|
||||
collected = append(collected, path.String()+":"+node.String())
|
||||
}))
|
||||
sort.Strings(collected)
|
||||
assert.EqualValues(t, expected, collected)
|
||||
}
|
||||
|
||||
//
|
||||
// unify the originTree with the destinationTree on the specified path
|
||||
//
|
||||
fullPath := generic.NewPathFromString("/O-A/O-B/O-C")
|
||||
collected = make([]string, 0, 10)
|
||||
generic.TreeUnifyPath(ctx, originTree, fullPath, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
|
||||
sort.Strings(collected)
|
||||
assert.EqualValues(t, []string{"/O-A/O-B/O-C:O-C=content O-C => /D-A/D-B/D-C:D-C=content O-C", "/O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "/O-A:O-A=content O-A => /D-A:D-A=content O-A"}, collected)
|
||||
assertTree(destinationTree, []string{"/D-A/D-B/D-C:D-C=content O-C", "/D-A/D-B:D-B=content O-B", "/D-A:D-A=content O-A"})
|
||||
|
||||
//
|
||||
// Add a node unrelated to the unification path
|
||||
//
|
||||
var unrelatedOriginPath path.Path
|
||||
{
|
||||
originTree.Apply(ctx, generic.NewPathFromString("/O-A/O-B"), generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
|
||||
assert.EqualValues(t, parent.Length()+1, node.GetCurrentPath().Length())
|
||||
unrelated := node.CreateChild(ctx)
|
||||
unrelated.Upsert(ctx)
|
||||
memory.SetContent(unrelated, "content "+unrelated.GetID().String())
|
||||
unrelated.Upsert(ctx)
|
||||
unrelatedOriginPath = unrelated.GetCurrentPath()
|
||||
assert.EqualValues(t, "/O-A/O-B/O-N", (unrelatedOriginPath.PathString().Join()))
|
||||
}))
|
||||
}
|
||||
|
||||
//
|
||||
// Replace the content of the last node
|
||||
//
|
||||
lastContent := "LAST"
|
||||
{
|
||||
lastPath := generic.NewPathFromString("/O-A/O-B/O-C")
|
||||
originTree.Apply(ctx, lastPath, generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
|
||||
memory.SetContent(node, lastContent)
|
||||
}))
|
||||
collected = make([]string, 0, 10)
|
||||
generic.TreeUnifyPath(ctx, originTree, lastPath, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
|
||||
sort.Strings(collected)
|
||||
assert.EqualValues(t, []string{"/O-A/O-B/O-C:O-C=LAST => /D-A/D-B/D-C:D-C=LAST", "/O-A/O-B:O-B=content O-B => /D-A/D-B:D-B=content O-B", "/O-A:O-A=content O-A => /D-A:D-A=content O-A"}, collected)
|
||||
assertTree(destinationTree, []string{"/D-A/D-B/D-C:D-C=LAST", "/D-A/D-B:D-B=content O-B", "/D-A:D-A=content O-A"})
|
||||
}
|
||||
//
|
||||
// Replace the content of the first node
|
||||
//
|
||||
firstContent := "FIRST"
|
||||
{
|
||||
firstPath := generic.NewPathFromString("/O-A")
|
||||
originTree.Apply(ctx, firstPath, generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
|
||||
memory.SetContent(node, firstContent)
|
||||
}))
|
||||
collected = make([]string, 0, 10)
|
||||
generic.TreeUnifyPath(ctx, originTree, firstPath, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
|
||||
sort.Strings(collected)
|
||||
assert.EqualValues(t, []string{"/O-A:O-A=" + firstContent + " => /D-A:D-A=" + firstContent}, collected)
|
||||
assertTree(destinationTree, []string{"/D-A/D-B/D-C:D-C=LAST", "/D-A/D-B:D-B=content O-B", "/D-A:D-A=FIRST"})
|
||||
}
|
||||
//
|
||||
// Replace the content of the second node
|
||||
//
|
||||
secondContent := "SECOND"
|
||||
{
|
||||
secondPath := generic.NewPathFromString("/O-A/O-B")
|
||||
originTree.Apply(ctx, secondPath, generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
|
||||
memory.SetContent(node, secondContent)
|
||||
}))
|
||||
collected = make([]string, 0, 10)
|
||||
generic.TreeUnifyPath(ctx, originTree, secondPath, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert))
|
||||
assert.EqualValues(t, []string{"/O-A:O-A=" + firstContent + " => /D-A:D-A=" + firstContent, "/O-A/O-B:O-B=" + secondContent + " => /D-A/D-B:D-B=" + secondContent}, collected)
|
||||
sort.Strings(collected)
|
||||
assertTree(destinationTree, []string{"/D-A/D-B/D-C:D-C=LAST", "/D-A/D-B:D-B=SECOND", "/D-A:D-A=FIRST"})
|
||||
}
|
||||
//
|
||||
// verify the node unrelated to the unification is still there
|
||||
//
|
||||
{
|
||||
var found bool
|
||||
originTree.Apply(ctx, unrelatedOriginPath, generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
|
||||
found = true
|
||||
}))
|
||||
assert.True(t, found)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnifyMirror(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
//
|
||||
// build and populate a tree
|
||||
//
|
||||
originTree := NewMemoryTree(ctx, "O")
|
||||
log := originTree.GetLogger()
|
||||
testTreeBuild(t, originTree, 2)
|
||||
|
||||
//
|
||||
// build an empty tree
|
||||
//
|
||||
destinationTree := NewMemoryTree(ctx, "D")
|
||||
|
||||
upsert := func(ctx context.Context, origin generic.NodeInterface, originPath path.Path, destination generic.NodeInterface, destinationPath path.Path) {
|
||||
assert.NotNil(t, origin.GetDriver().(*memory.Driver))
|
||||
assert.NotNil(t, destination.GetDriver().(*memory.Driver))
|
||||
originPath = originPath.Append(origin)
|
||||
destinationPath = destinationPath.Append(destination)
|
||||
what := fmt.Sprintf("%s:%s => %s:%s", originPath, origin.GetSelf(), destinationPath, destination.GetSelf())
|
||||
log.Trace("mirror upsert: %T => %T | %s", origin, destination, what)
|
||||
}
|
||||
delete := func(ctx context.Context, destination generic.NodeInterface, destinationPath path.Path) {
|
||||
assert.NotNil(t, destination.GetDriver().(*memory.Driver))
|
||||
destinationPath = destinationPath.Append(destination)
|
||||
log.Trace("mirror delete: %T | %s:%s", destination, destinationPath, destination)
|
||||
}
|
||||
|
||||
var sameTree func(origin, destination generic.NodeInterface) bool
|
||||
sameTree = func(origin, destination generic.NodeInterface) bool {
|
||||
what := origin.GetCurrentPath().String() + ":" + origin.GetSelf().String() + " => " + destination.GetCurrentPath().String() + ":" + destination.GetSelf().String()
|
||||
log.Trace("sameTree: %T => %T | %s", origin.GetSelf(), destination.GetSelf(), what)
|
||||
if origin.GetMappedID() != destination.GetID() {
|
||||
log.Trace("sameTree: different: %s != %s", origin.GetMappedID(), destination.GetID())
|
||||
return false
|
||||
}
|
||||
originChildren := origin.GetChildren()
|
||||
destinationChildren := destination.GetChildren()
|
||||
if len(originChildren) != len(destinationChildren) {
|
||||
log.Trace("sameTree: different: length %v != %v", len(originChildren), len(destinationChildren))
|
||||
return false
|
||||
}
|
||||
|
||||
for _, originChild := range originChildren {
|
||||
destinationChild := destination.GetChild(originChild.GetMappedID())
|
||||
if destinationChild == generic.NilNode {
|
||||
log.Trace("sameTree: different: %s not found", originChild.GetMappedID())
|
||||
return false
|
||||
}
|
||||
if !sameTree(originChild, destinationChild) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
//
|
||||
// unify the originTree with the destinationTree
|
||||
//
|
||||
assert.False(t, sameTree(originTree.GetRoot(), destinationTree.GetRoot()))
|
||||
generic.TreeUnify(ctx, originTree, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert).SetDelete(delete))
|
||||
assert.True(t, sameTree(originTree.GetRoot(), destinationTree.GetRoot()))
|
||||
generic.TreeUnify(ctx, originTree, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert).SetDelete(delete))
|
||||
assert.True(t, sameTree(originTree.GetRoot(), destinationTree.GetRoot()))
|
||||
|
||||
{
|
||||
addNode := func(tree generic.TreeInterface, pathString string) {
|
||||
tree.Apply(ctx, generic.NewPathFromString(pathString), generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
|
||||
new := node.CreateChild(ctx)
|
||||
new.Upsert(ctx)
|
||||
memory.SetContent(new, "content "+new.GetID().String())
|
||||
new.Upsert(ctx)
|
||||
log.Trace("add: %s", parent.ReadableString())
|
||||
log.Trace("add: %s", node.GetCurrentPath().ReadableString())
|
||||
log.Trace("add: %s", new.GetCurrentPath().ReadableString())
|
||||
}))
|
||||
}
|
||||
|
||||
for _, testCase := range []struct {
|
||||
existingPath string
|
||||
newPath string
|
||||
}{
|
||||
{
|
||||
existingPath: "/O-A/O-B",
|
||||
newPath: "/D-A/D-B/D-N",
|
||||
},
|
||||
{
|
||||
existingPath: "/O-A",
|
||||
newPath: "/D-A/D-O",
|
||||
},
|
||||
{
|
||||
existingPath: "/O-A/O-J/O-K",
|
||||
newPath: "/D-A/D-J/D-K/D-P",
|
||||
},
|
||||
} {
|
||||
t.Run("add"+testCase.newPath, func(t *testing.T) {
|
||||
destinationPath := generic.NewPathFromString(testCase.newPath)
|
||||
addNode(originTree, testCase.existingPath)
|
||||
assert.False(t, sameTree(originTree.GetRoot(), destinationTree.GetRoot()))
|
||||
assert.False(t, destinationTree.Exists(ctx, destinationPath), destinationPath.String())
|
||||
generic.TreeUnify(ctx, originTree, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert).SetDelete(delete))
|
||||
assert.True(t, destinationTree.Exists(ctx, destinationPath), destinationPath.String())
|
||||
assert.True(t, sameTree(originTree.GetRoot(), destinationTree.GetRoot()))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
deleteNode := func(tree generic.TreeInterface, toDelete path.Path) {
|
||||
tree.Apply(ctx, toDelete, generic.NewApplyOptions(func(ctx context.Context, parent, path path.Path, node generic.NodeInterface) {
|
||||
node.Delete(ctx)
|
||||
}))
|
||||
}
|
||||
|
||||
for _, testCase := range []struct {
|
||||
originPath string
|
||||
destinationPath string
|
||||
}{
|
||||
{
|
||||
originPath: "/O-A/O-F",
|
||||
destinationPath: "/D-A/D-F",
|
||||
},
|
||||
} {
|
||||
t.Run("delete"+testCase.originPath, func(t *testing.T) {
|
||||
originPath := generic.NewPathFromString(testCase.originPath)
|
||||
destinationPath := generic.NewPathFromString(testCase.destinationPath)
|
||||
assert.True(t, originTree.Exists(ctx, originPath))
|
||||
assert.True(t, destinationTree.Exists(ctx, destinationPath), destinationPath.String())
|
||||
deleteNode(originTree, originPath)
|
||||
assert.False(t, originTree.Exists(ctx, originPath))
|
||||
generic.TreeUnify(ctx, originTree, destinationTree, generic.NewUnifyOptions(destinationTree).SetUpsert(upsert).SetDelete(delete))
|
||||
assert.False(t, destinationTree.Exists(ctx, destinationPath), destinationPath.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeParallelApplyFound(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, testCase := range []struct {
|
||||
start string
|
||||
destination string
|
||||
expected []string
|
||||
expectedRemapped string
|
||||
}{
|
||||
{
|
||||
start: "/O-A/O-B",
|
||||
destination: "O-C",
|
||||
expected: []string{"/O-A/O-B => /D-A/D-B", "/O-A/O-B/O-C => /D-A/D-B/D-C"},
|
||||
expectedRemapped: "/D-A/D-B/D-C",
|
||||
},
|
||||
{
|
||||
start: "/O-A/O-B/O-D",
|
||||
destination: ".",
|
||||
expected: []string{"/O-A/O-B/O-D => /D-A/D-B/D-D"},
|
||||
expectedRemapped: "/D-A/D-B/D-D",
|
||||
},
|
||||
{
|
||||
start: ".",
|
||||
destination: ".",
|
||||
expected: []string{" => "},
|
||||
expectedRemapped: "",
|
||||
},
|
||||
{
|
||||
start: "/O-A/O-B/O-C",
|
||||
destination: "../O-D",
|
||||
expected: []string{"/O-A/O-B => /D-A/D-B", "/O-A/O-B/O-D => /D-A/D-B/D-D"},
|
||||
expectedRemapped: "/D-A/D-B/D-D",
|
||||
},
|
||||
{
|
||||
start: "/",
|
||||
destination: ".",
|
||||
expected: []string{" => "},
|
||||
expectedRemapped: "",
|
||||
},
|
||||
} {
|
||||
t.Run(" "+testCase.start+" => "+testCase.destination, func(t *testing.T) {
|
||||
originTree := NewMemoryTree(ctx, "O")
|
||||
testTreeBuild(t, originTree, 2)
|
||||
destinationTree := NewMemoryTree(ctx, "D")
|
||||
|
||||
//
|
||||
// Mirror two trees
|
||||
//
|
||||
generic.TreeMirror(ctx, originTree, destinationTree, generic.NewPathFromString("/"), generic.NewMirrorOptions())
|
||||
//
|
||||
// collect all nodes traversed by the apply function along testCase.destination
|
||||
//
|
||||
collected := make([]string, 0, 10)
|
||||
collect := func(ctx context.Context, origin, destination generic.NodeInterface) {
|
||||
collected = append(collected, fmt.Sprintf("%s => %s", origin.GetCurrentPath().String(), destination.GetCurrentPath().String()))
|
||||
}
|
||||
//
|
||||
// get to testCase.start and from there run the apply function to reach testCase.destination
|
||||
//
|
||||
nodeApply := func(ctx context.Context, origin, destination generic.NodeInterface) {
|
||||
assert.True(t, generic.NodeParallelApply(ctx, origin, generic.NewPathFromString(testCase.destination), destination, generic.NewParallelApplyOptions(collect).SetWhere(generic.ApplyEachNode)))
|
||||
}
|
||||
assert.True(t, generic.TreeParallelApply(ctx, originTree, generic.NewPathFromString(testCase.start), destinationTree, generic.NewParallelApplyOptions(nodeApply)))
|
||||
assert.EqualValues(t, testCase.expected, collected)
|
||||
|
||||
//
|
||||
// test TreePathRemap
|
||||
//
|
||||
remappedPath := generic.TreePathRemap(ctx, originTree, generic.NewPathFromString(filepath.Join(testCase.start, testCase.destination)), destinationTree)
|
||||
assert.EqualValues(t, testCase.expectedRemapped, remappedPath.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeParallelApplyNoRemap(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, testCase := range []struct {
|
||||
noremap bool
|
||||
start string
|
||||
expected []string
|
||||
expectedRemapped string
|
||||
}{
|
||||
{
|
||||
noremap: false,
|
||||
start: "/O-A/O-B",
|
||||
expected: []string{" => ", "/O-A => /D-A", "/O-A/O-B => /D-A/D-B"},
|
||||
expectedRemapped: "/D-A/D-B",
|
||||
},
|
||||
{
|
||||
noremap: true,
|
||||
start: "/O-A/O-B",
|
||||
expected: []string{" => ", "/O-A => /O-A", "/O-A/O-B => /O-A/O-B"},
|
||||
expectedRemapped: "/O-A/O-B",
|
||||
},
|
||||
} {
|
||||
t.Run(fmt.Sprintf("noremap=%v,start=%s", testCase.noremap, testCase.start), func(t *testing.T) {
|
||||
originName := "O"
|
||||
originTree := NewMemoryTree(ctx, originName)
|
||||
testTreeBuild(t, originTree, 2)
|
||||
var destinationName string
|
||||
if testCase.noremap {
|
||||
destinationName = originName
|
||||
} else {
|
||||
destinationName = "D"
|
||||
}
|
||||
destinationTree := NewMemoryTree(ctx, destinationName)
|
||||
|
||||
//
|
||||
// Mirror two trees
|
||||
//
|
||||
generic.TreeMirror(ctx, originTree, destinationTree, generic.NewPathFromString("/"), generic.NewMirrorOptions())
|
||||
//
|
||||
// collect all nodes traversed by the apply function along testCase.destination
|
||||
//
|
||||
collected := make([]string, 0, 10)
|
||||
collect := func(ctx context.Context, origin, destination generic.NodeInterface) {
|
||||
collected = append(collected, fmt.Sprintf("%s => %s", origin.GetCurrentPath(), destination.GetCurrentPath()))
|
||||
}
|
||||
//
|
||||
// get to testCase.start and from there run the apply function to reach testCase.destination
|
||||
//
|
||||
assert.True(t, generic.TreeParallelApply(ctx, originTree, generic.NewPathFromString(testCase.start), destinationTree, generic.NewParallelApplyOptions(collect).SetWhere(generic.ApplyEachNode).SetNoRemap(testCase.noremap)))
|
||||
assert.EqualValues(t, testCase.expected, collected)
|
||||
|
||||
//
|
||||
// test TreePathRemap
|
||||
//
|
||||
remappedPath := generic.TreePathRemap(ctx, originTree, generic.NewPathFromString(testCase.start), destinationTree)
|
||||
assert.EqualValues(t, testCase.expectedRemapped, remappedPath.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeParallelApplyNotFound(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
for _, testCase := range []struct {
|
||||
start string
|
||||
destination string
|
||||
}{
|
||||
{
|
||||
start: "/O-A",
|
||||
destination: "O-B/???",
|
||||
},
|
||||
{
|
||||
start: "/O-A/O-B",
|
||||
destination: "???",
|
||||
},
|
||||
{
|
||||
start: "/O-A/O-B",
|
||||
destination: "../???",
|
||||
},
|
||||
} {
|
||||
t.Run(" "+testCase.start+" => "+testCase.destination, func(t *testing.T) {
|
||||
originTree := NewMemoryTree(ctx, "O")
|
||||
testTreeBuild(t, originTree, 2)
|
||||
destinationTree := NewMemoryTree(ctx, "D")
|
||||
|
||||
//
|
||||
// Mirror two trees
|
||||
//
|
||||
generic.TreeMirror(ctx, originTree, destinationTree, generic.NewPathFromString("/"), generic.NewMirrorOptions())
|
||||
//
|
||||
// get to testCase.start and from there run the apply function to reach testCase.destination
|
||||
//
|
||||
var called bool
|
||||
nodeApply := func(ctx context.Context, origin, destination generic.NodeInterface) {
|
||||
called = true
|
||||
found := generic.NodeParallelApply(ctx, origin, generic.NewPathFromString(testCase.destination), destination, generic.NewParallelApplyOptions(nil))
|
||||
assert.False(t, found)
|
||||
}
|
||||
assert.True(t, generic.TreeParallelApply(ctx, originTree, generic.NewPathFromString(testCase.start), destinationTree, generic.NewParallelApplyOptions(nodeApply)))
|
||||
assert.True(t, called)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue