From cfd80ed3d881296ced139f2bc4017e0f39b85e6f Mon Sep 17 00:00:00 2001 From: Akash Kumar Date: Sun, 12 May 2024 23:46:07 +0530 Subject: [PATCH] add tests Signed-off-by: Akash Kumar --- pkg/3rdparty/mvs/mvs_test.go | 2 +- pkg/client/client.go | 26 +++++----- pkg/mvs/mvs.go | 54 ++++++++++++++++++++- pkg/mvs/mvs_test.go | 93 ++++++++++++++++++++++++++---------- pkg/package/modfile.go | 20 ++++++++ pkg/package/modfile_test.go | 13 +++++ pkg/semver/semver.go | 58 +++++++++++++++++++--- pkg/semver/semver_test.go | 40 +++++++--------- 8 files changed, 235 insertions(+), 71 deletions(-) diff --git a/pkg/3rdparty/mvs/mvs_test.go b/pkg/3rdparty/mvs/mvs_test.go index b18ee2de..92b6c837 100644 --- a/pkg/3rdparty/mvs/mvs_test.go +++ b/pkg/3rdparty/mvs/mvs_test.go @@ -212,7 +212,7 @@ upgrade* A: A B5.hidden C3 name: down1 A: B2 -B1: C1 +B1: C2 B2: C2 build A: A B2 C2 downgrade A C1: A B1 C1 diff --git a/pkg/client/client.go b/pkg/client/client.go index e7bb1d4d..9655c727 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -1255,7 +1255,7 @@ func (c *KpmClient) InitGraphAndDownloadDeps(kclPkg *pkg.KclPkg) (*pkg.Dependenc return nil, nil, err } - changedDeps, err := c.downloadDeps(kclPkg.ModFile.Dependencies, kclPkg.Dependencies, depGraph, kclPkg.HomePath, root) + changedDeps, err := c.DownloadDeps(kclPkg.ModFile.Dependencies, kclPkg.Dependencies, depGraph, kclPkg.HomePath, root) if err != nil { return nil, nil, err } @@ -1287,7 +1287,7 @@ func (c *KpmClient) dependencyExists(dep *pkg.Dependency, lockDeps *pkg.Dependen } // downloadDeps will download all the dependencies of the current kcl package. -func (c *KpmClient) downloadDeps(deps pkg.Dependencies, lockDeps pkg.Dependencies, depGraph graph.Graph[module.Version, module.Version], pkghome string, parent module.Version) (*pkg.Dependencies, error) { +func (c *KpmClient) DownloadDeps(deps pkg.Dependencies, lockDeps pkg.Dependencies, depGraph graph.Graph[module.Version, module.Version], pkghome string, parent module.Version) (*pkg.Dependencies, error) { newDeps := pkg.Dependencies{ Deps: make(map[string]pkg.Dependency), @@ -1371,20 +1371,22 @@ func (c *KpmClient) downloadDeps(deps pkg.Dependencies, lockDeps pkg.Dependencie return nil, err } - err = depGraph.AddEdge(parent, source) - if err != nil { - if err == graph.ErrEdgeCreatesCycle { - return nil, reporter.NewErrorEvent( - reporter.CircularDependencyExist, - nil, - fmt.Sprintf("adding %s as a dependency results in a cycle", source), - ) + if parent != (module.Version{}) { + err = depGraph.AddEdge(parent, source) + if err != nil { + if err == graph.ErrEdgeCreatesCycle { + return nil, reporter.NewErrorEvent( + reporter.CircularDependencyExist, + nil, + fmt.Sprintf("adding %s as a dependency results in a cycle", source), + ) + } + return nil, err } - return nil, err } // Download the indirect dependencies. - nested, err := c.downloadDeps(deppkg.ModFile.Dependencies, lockDeps, depGraph, deppkg.HomePath, source) + nested, err := c.DownloadDeps(deppkg.ModFile.Dependencies, lockDeps, depGraph, deppkg.HomePath, source) if err != nil { return nil, err } diff --git a/pkg/mvs/mvs.go b/pkg/mvs/mvs.go index be89bdec..307cfb67 100644 --- a/pkg/mvs/mvs.go +++ b/pkg/mvs/mvs.go @@ -6,6 +6,7 @@ import ( "github.com/dominikbraun/graph" "github.com/hashicorp/go-version" "golang.org/x/mod/module" + "kcl-lang.io/kpm/pkg/client" "kcl-lang.io/kpm/pkg/errors" "kcl-lang.io/kpm/pkg/git" "kcl-lang.io/kpm/pkg/oci" @@ -16,6 +17,8 @@ import ( type ReqsGraph struct { graph.Graph[module.Version, module.Version] + kpmClient *client.KpmClient + kpmPkg *pkg.KclPkg } func (r ReqsGraph) Max(_, v1, v2 string) string { @@ -50,10 +53,33 @@ func (r ReqsGraph) Upgrade(m module.Version) (module.Version, error) { return m, nil } - m.Version, err = semver.LatestVersion(releases) + m.Version, err = semver.LatestCompatibleVersion(releases, m.Version) if err != nil { return module.Version{}, err } + _, err = r.Vertex(m) + if err == graph.ErrVertexNotFound { + d := pkg.Dependency{ + Name: m.Path, + Version: m.Version, + } + d.FullName = d.GenDepFullName() + for sourceType, uri := range properties.Attributes { + d.Source, err = pkg.GenSource(sourceType, uri, m.Version) + if err != nil { + return module.Version{}, err + } + } + deps := pkg.Dependencies{ + Deps: map[string]pkg.Dependency{ + m.Path: d, + }, + } + lockDeps := pkg.Dependencies{ + Deps: make(map[string]pkg.Dependency), + } + r.kpmClient.DownloadDeps(deps, lockDeps, r.Graph, r.kpmPkg.HomePath, module.Version{}) + } return m, nil } @@ -72,10 +98,34 @@ func (r ReqsGraph) Previous(m module.Version) (module.Version, error) { return m, nil } - m.Version, err = semver.OldestVersion(releases) + m.Version, err = semver.LeastOldCompatibleVersion(releases, m.Version) if err != nil { return module.Version{}, err } + + _, err = r.Vertex(m) + if err == graph.ErrVertexNotFound { + d := pkg.Dependency{ + Name: m.Path, + Version: m.Version, + } + d.FullName = d.GenDepFullName() + for sourceType, uri := range properties.Attributes { + d.Source, err = pkg.GenSource(sourceType, uri, m.Version) + if err != nil { + return module.Version{}, err + } + } + deps := pkg.Dependencies{ + Deps: map[string]pkg.Dependency{ + m.Path: d, + }, + } + lockDeps := pkg.Dependencies{ + Deps: make(map[string]pkg.Dependency), + } + r.kpmClient.DownloadDeps(deps, lockDeps, r.Graph, r.kpmPkg.HomePath, module.Version{}) + } return m, nil } diff --git a/pkg/mvs/mvs_test.go b/pkg/mvs/mvs_test.go index b9cb8006..3dce50cb 100644 --- a/pkg/mvs/mvs_test.go +++ b/pkg/mvs/mvs_test.go @@ -1,7 +1,6 @@ package mvs import ( - "fmt" "os" "path/filepath" "testing" @@ -44,6 +43,8 @@ func TestRequired(t *testing.T) { reqs := ReqsGraph{ depGraph, + kpmcli, + kclPkg, } req, err := reqs.Required(module.Version{Path: "aaa", Version: "0.0.1"}) @@ -57,7 +58,7 @@ func TestRequired(t *testing.T) { assert.Equal(t, req, expectedReqs) } -func TestMinBuildList(t *testing.T) { +func TestUpgrade(t *testing.T) { pkg_path := getTestDir("test_with_external_deps") assert.Equal(t, utils.DirExists(filepath.Join(pkg_path, "kcl.mod")), true) kpmcli, err := client.NewKpmClient() @@ -70,39 +71,27 @@ func TestMinBuildList(t *testing.T) { reqs := ReqsGraph{ depGraph, + kpmcli, + kclPkg, } target := module.Version{Path: kclPkg.GetPkgName(), Version: kclPkg.GetPkgVersion()} - - req, err := mvs.BuildList([]module.Version{target}, reqs) + upgradeList := []module.Version{ + {Path: "argo-cd-order", Version: "0.2.0"}, + {Path: "helloworld", Version: "0.1.1"}, + } + upgrade, err := mvs.Upgrade(target, reqs, upgradeList...) assert.Equal(t, err, nil) expectedReqs := []module.Version{ {Path: "test_with_external_deps", Version: "0.0.1"}, - {Path: "argo-cd-order", Version: "0.1.2"}, - {Path: "helloworld", Version: "0.1.0"}, - {Path: "json_merge_patch", Version: "0.1.0"}, - {Path: "k8s", Version: "1.29"}, - {Path: "podinfo", Version: "0.1.1"}, - } - assert.Equal(t, req, expectedReqs) - - base := []string{target.Path} - for depName := range kclPkg.Dependencies.Deps { - base = append(base, depName) - } - req, err = mvs.Req(target, base, reqs) - assert.Equal(t, err, nil) - - expectedReqs = []module.Version{ - {Path: "argo-cd-order", Version: "0.1.2"}, - {Path: "helloworld", Version: "0.1.0"}, + {Path: "argo-cd-order", Version: "0.2.0"}, + {Path: "helloworld", Version: "0.1.1"}, {Path: "json_merge_patch", Version: "0.1.0"}, {Path: "k8s", Version: "1.29"}, {Path: "podinfo", Version: "0.1.1"}, - {Path: "test_with_external_deps", Version: "0.0.1"}, } - assert.Equal(t, req, expectedReqs) + assert.Equal(t, upgrade, expectedReqs) } func TestUpgradeToLatest(t *testing.T) { @@ -118,6 +107,8 @@ func TestUpgradeToLatest(t *testing.T) { reqs := ReqsGraph{ depGraph, + kpmcli, + kclPkg, } upgrade, err := reqs.Upgrade(module.Version{Path: "k8s", Version: "1.27"}) @@ -138,13 +129,24 @@ func TestUpgradeAllToLatest(t *testing.T) { reqs := ReqsGraph{ depGraph, + kpmcli, + kclPkg, } target := module.Version{Path: kclPkg.GetPkgName(), Version: kclPkg.GetPkgVersion()} - fmt.Println(target) + upgrade, err := mvs.UpgradeAll(target, reqs) assert.Equal(t, err, nil) - assert.Equal(t, upgrade, module.Version{Path: "k8s", Version: "1.29"}) + + expectedReqs := []module.Version{ + {Path: "test_with_external_deps", Version: "0.0.1"}, + {Path: "argo-cd-order", Version: "0.2.0"}, + {Path: "helloworld", Version: "0.1.2"}, + {Path: "json_merge_patch", Version: "0.1.1"}, + {Path: "k8s", Version: "1.29"}, + {Path: "podinfo", Version: "0.1.1"}, + } + assert.Equal(t, upgrade, expectedReqs) } func TestPrevious(t *testing.T) { @@ -160,6 +162,8 @@ func TestPrevious(t *testing.T) { reqs := ReqsGraph{ depGraph, + kpmcli, + kclPkg, } downgrade, err := reqs.Previous(module.Version{Path: "k8s", Version: "1.27"}) @@ -180,6 +184,8 @@ func TestUpgradePreviousOfLocalDependency(t *testing.T) { reqs := ReqsGraph{ depGraph, + kpmcli, + kclPkg, } upgrade, err := reqs.Upgrade(module.Version{Path: "bbb", Version: "0.0.1"}) @@ -190,3 +196,38 @@ func TestUpgradePreviousOfLocalDependency(t *testing.T) { assert.Equal(t, err, nil) assert.Equal(t, downgrade, module.Version{Path: "bbb", Version: "0.0.1"}) } + +func TestDowngrade(t *testing.T) { + pkg_path := getTestDir("test_with_external_deps") + assert.Equal(t, utils.DirExists(filepath.Join(pkg_path, "kcl.mod")), true) + kpmcli, err := client.NewKpmClient() + assert.Equal(t, err, nil) + kclPkg, err := kpmcli.LoadPkgFromPath(pkg_path) + assert.Equal(t, err, nil) + + _, depGraph, err := kpmcli.InitGraphAndDownloadDeps(kclPkg) + assert.Equal(t, err, nil) + + reqs := ReqsGraph{ + depGraph, + kpmcli, + kclPkg, + } + + target := module.Version{Path: kclPkg.GetPkgName(), Version: kclPkg.GetPkgVersion()} + downgradeList := []module.Version{ + {Path: "k8s", Version: "1.17"}, + } + downgrade, err := mvs.Downgrade(target, reqs, downgradeList...) + assert.Equal(t, err, nil) + + expectedReqs := []module.Version{ + {Path: "test_with_external_deps", Version: "0.0.1"}, + {Path: "argo-cd-order", Version: "0.2.0"}, + {Path: "helloworld", Version: "0.1.1"}, + {Path: "json_merge_patch", Version: "0.1.0"}, + {Path: "k8s", Version: "1.27"}, + {Path: "podinfo", Version: "0.1.1"}, + } + assert.Equal(t, downgrade, expectedReqs) +} diff --git a/pkg/package/modfile.go b/pkg/package/modfile.go index 55e6cb2a..ef68d7b1 100644 --- a/pkg/package/modfile.go +++ b/pkg/package/modfile.go @@ -259,6 +259,26 @@ func (dep *Dependency) GetDownloadPath() string { return "" } +func GenSource(sourceType string, uri string, tagName string) (Source, error) { + source := Source{} + if sourceType == GIT { + source.Git = &Git{ + Url: uri, + Tag: tagName, + } + return source, nil + } + if sourceType == OCI { + oci := Oci{} + _, err := oci.FromString(uri + ":" + tagName) + if err != nil { + return Source{}, err + } + source.Oci = &oci + } + return source, nil +} + // GetSourceType will get the source type of a dependency. func (dep *Dependency) GetSourceType() string { if dep.Source.Git != nil { diff --git a/pkg/package/modfile_test.go b/pkg/package/modfile_test.go index f3f05e2e..ebc3bd0d 100644 --- a/pkg/package/modfile_test.go +++ b/pkg/package/modfile_test.go @@ -235,3 +235,16 @@ func TestGetFilePath(t *testing.T) { assert.Equal(t, mfile.GetModFilePath(), filepath.Join(testPath, MOD_FILE)) assert.Equal(t, mfile.GetModLockFilePath(), filepath.Join(testPath, MOD_LOCK_FILE)) } + +func TestGenSource(t *testing.T) { + src, err := GenSource("git", "https://github.com/kcl-lang/kcl", "0.8.7") + assert.Equal(t, err, nil) + assert.Equal(t, src.Git.Url, "https://github.com/kcl-lang/kcl") + assert.Equal(t, src.Git.Tag, "0.8.7") + + src, err = GenSource("oci", "oci://ghcr.io/kcl-lang/k8s", "1.24") + assert.Equal(t, err, nil) + assert.Equal(t, src.Oci.Reg, "ghcr.io") + assert.Equal(t, src.Oci.Repo, "kcl-lang/k8s") + assert.Equal(t, src.Oci.Tag, "1.24") +} \ No newline at end of file diff --git a/pkg/semver/semver.go b/pkg/semver/semver.go index 53e0dd21..0e4f449e 100644 --- a/pkg/semver/semver.go +++ b/pkg/semver/semver.go @@ -27,21 +27,65 @@ func LatestVersion(versions []string) (string, error) { return latest.Original(), nil } -func OldestVersion(versions []string) (string, error) { - var oldest *version.Version +// LeastOldVersion returns the version that is most recent and less than the base version. +func LeastOldVersion(versions []string, baseVersion string) (string, error) { + base, err := version.NewVersion(baseVersion) + if err != nil { + return "", fmt.Errorf("invalid base version: %v", err) + } + + var leastOld *version.Version for _, v := range versions { ver, err := version.NewVersion(v) if err != nil { return "", reporter.NewErrorEvent(reporter.FailedParseVersion, err, fmt.Sprintf("failed to parse version %s", v)) } - if oldest == nil || ver.LessThan(oldest) { - oldest = ver + + // Only consider versions less than the base version + if ver.LessThan(base) { + if leastOld == nil || ver.GreaterThan(leastOld) { + leastOld = ver + } } } - if oldest == nil { + if leastOld == nil { return "", errors.InvalidVersionFormat } - return oldest.Original(), nil -} \ No newline at end of file + return leastOld.Original(), nil +} + +func filterCompatibleVersions(versions []string, baseVersion string) ([]string, error) { + base, err := version.NewVersion(baseVersion) + if err != nil { + return nil, fmt.Errorf("invalid base version: %v", err) + } + var compatibleVersions []string + for _, v := range versions { + ver, err := version.NewVersion(v) + if err != nil { + continue // skip versions that fail to parse + } + if ver.Segments()[0] == base.Segments()[0] && ver.Prerelease() == "" { + compatibleVersions = append(compatibleVersions, ver.Original()) + } + } + return compatibleVersions, nil +} + +func LatestCompatibleVersion(versions []string, baseVersion string) (string, error) { + compatibleVersions, err := filterCompatibleVersions(versions, baseVersion) + if err != nil { + return "", err + } + return LatestVersion(compatibleVersions) +} + +func LeastOldCompatibleVersion(versions []string, baseVersion string) (string, error) { + compatibleVersions, err := filterCompatibleVersions(versions, baseVersion) + if err != nil { + return "", err + } + return LeastOldVersion(compatibleVersions, baseVersion) +} diff --git a/pkg/semver/semver_test.go b/pkg/semver/semver_test.go index 560b4d99..4ab8adfd 100644 --- a/pkg/semver/semver_test.go +++ b/pkg/semver/semver_test.go @@ -39,34 +39,28 @@ func TestTheLatestTagWithMissingVersion(t *testing.T) { assert.Equal(t, latest, "5.5") } -func TestOldestVersion(t *testing.T) { - oldest, err := OldestVersion([]string{"1.2.3", "1.4.0", "1.3.5", "1.0.0"}) +func TestLeastOldVersion(t *testing.T) { + leastOld, err := LeastOldVersion([]string{"1.2.3", "1.4.0", "2.0.0", "1.3.5", "1.0.0"}, "1.2.0") assert.Equal(t, err, nil) - assert.Equal(t, oldest, "1.0.0") + assert.Equal(t, leastOld, "1.0.0") - oldest, err = OldestVersion([]string{}) - assert.Equal(t, err, errors.InvalidVersionFormat) - assert.Equal(t, oldest, "") - - oldest, err = OldestVersion([]string{"invalid_version"}) - assert.Equal(t, err.Error(), "failed to parse version invalid_version\nMalformed version: invalid_version\n") - assert.Equal(t, oldest, "") - - oldest, err = OldestVersion([]string{"1.2.3", "1.4.0", "1.3.5", "invalid_version"}) - assert.Equal(t, err.Error(), "failed to parse version invalid_version\nMalformed version: invalid_version\n") - assert.Equal(t, oldest, "") -} - -func TestOldestVersionWithVariousFormats(t *testing.T) { - oldest, err := OldestVersion([]string{"2.2", "2.4.5", "2.3.9", "2.1.0", "2.0"}) + leastOld, err = LeastOldVersion([]string{"2.2.0", "2.4.0", "3.0.0", "2.3.5"}, "2.5.0") assert.Equal(t, err, nil) - assert.Equal(t, oldest, "2.0") + assert.Equal(t, leastOld, "2.4.0") +} - oldest, err = OldestVersion([]string{"0.1", "0.1.1", "0.1.2-beta", "0.0.9"}) +func TestFilterCompatibleVersions(t *testing.T) { + compatible, err := filterCompatibleVersions([]string{"1.2.3", "1.4.0", "2.0.0", "1.3.5", "1.0.0"}, "1.2.0") assert.Equal(t, err, nil) - assert.Equal(t, oldest, "0.0.9") + expCompatible := []string{"1.2.3", "1.4.0", "1.3.5", "1.0.0"} + for i, v := range compatible { + assert.Equal(t, v, expCompatible[i]) + } - oldest, err = OldestVersion([]string{"3.3.3", "3.2", "3.1", "3.0.0"}) + compatible, err = filterCompatibleVersions([]string{"2.2.0", "2.4.0", "3.0.0", "2.3.5"}, "2.0.0") assert.Equal(t, err, nil) - assert.Equal(t, oldest, "3.0.0") + expCompatible = []string{"2.2.0", "2.4.0", "2.3.5"} + for i, v := range compatible { + assert.Equal(t, v, expCompatible[i]) + } }