From 9fc8709e7b87e5d72754e5a1b9b8ca8ed014712c Mon Sep 17 00:00:00 2001 From: Maxim Slipenko Date: Sun, 19 Jan 2025 12:09:24 +0300 Subject: [PATCH] tests: add parseActionsFromRepoChanges tests --- go.mod | 3 + pkg/repos/pull.go | 72 ++++++++++------- pkg/repos/utils_test.go | 168 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 213 insertions(+), 30 deletions(-) create mode 100644 pkg/repos/utils_test.go diff --git a/go.mod b/go.mod index 69cc341..8a18050 100644 --- a/go.mod +++ b/go.mod @@ -21,6 +21,7 @@ require ( github.com/muesli/reflow v0.3.0 github.com/pelletier/go-toml/v2 v2.1.0 github.com/schollz/progressbar/v3 v3.13.1 + github.com/stretchr/testify v1.9.0 github.com/urfave/cli/v2 v2.25.7 github.com/vmihailenco/msgpack/v5 v5.3.5 go.elara.ws/logger v0.0.0-20230421022458-e80700db2090 @@ -56,6 +57,7 @@ require ( github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect github.com/dsnet/compress v0.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect @@ -92,6 +94,7 @@ require ( github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect github.com/pierrec/lz4/v4 v4.1.15 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/pkg/repos/pull.go b/pkg/repos/pull.go index c8202ef..71e4d70 100644 --- a/pkg/repos/pull.go +++ b/pkg/repos/pull.go @@ -31,6 +31,7 @@ import ( "github.com/go-git/go-billy/v5/osfs" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/object" "github.com/pelletier/go-toml/v2" "go.elara.ws/vercmp" "mvdan.cc/sh/v3/expand" @@ -52,8 +53,8 @@ const ( ) type action struct { - Type actionType - File string + Type actionType + FilePath string } // Pull pulls the provided repositories. If a repo doesn't exist, it will be cloned @@ -178,20 +179,10 @@ func (rs *Repos) Pull(ctx context.Context, repos []types.Repo) error { return nil } -func (rs *Repos) processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, w *git.Worktree, old, new *plumbing.Reference) error { - oldCommit, err := r.CommitObject(old.Hash()) - if err != nil { - return err - } - - newCommit, err := r.CommitObject(new.Hash()) - if err != nil { - return err - } - +func parseActionsFromRepoChanges(ctx context.Context, r *git.Repository, w *git.Worktree, oldCommit, newCommit *object.Commit) ([]action, error) { patch, err := oldCommit.Patch(newCommit) if err != nil { - return err + return nil, err } var actions []action @@ -204,40 +195,61 @@ func (rs *Repos) processRepoChanges(ctx context.Context, repo types.Repo, r *git if to == nil { actions = append(actions, action{ - Type: actionDelete, - File: from.Path(), + Type: actionDelete, + FilePath: from.Path(), }) } else if from == nil { actions = append(actions, action{ - Type: actionUpdate, - File: to.Path(), + Type: actionUpdate, + FilePath: to.Path(), }) } else { if from.Path() != to.Path() { actions = append(actions, action{ - Type: actionDelete, - File: from.Path(), + Type: actionDelete, + FilePath: from.Path(), }, action{ - Type: actionUpdate, - File: to.Path(), + Type: actionUpdate, + FilePath: to.Path(), }, ) } else { actions = append(actions, action{ - Type: actionUpdate, - File: to.Path(), + Type: actionUpdate, + FilePath: to.Path(), }) } } } + return actions, nil +} + +func (rs *Repos) processRepoChanges(ctx context.Context, repo types.Repo, r *git.Repository, w *git.Worktree, old, new *plumbing.Reference) error { + oldCommit, err := r.CommitObject(old.Hash()) + if err != nil { + return err + } + + newCommit, err := r.CommitObject(new.Hash()) + if err != nil { + return err + } + + actions, err := parseActionsFromRepoChanges( + ctx, r, w, oldCommit, newCommit, + ) + if err != nil { + return err + } + repoDir := w.Filesystem.Root() parser := syntax.NewParser() for _, action := range actions { - env := append(os.Environ(), "scriptdir="+filepath.Dir(filepath.Join(repoDir, action.File))) + env := append(os.Environ(), "scriptdir="+filepath.Dir(filepath.Join(repoDir, action.FilePath))) runner, err := interp.New( interp.Env(expand.ListEnviron(env...)), interp.ExecHandler(handlers.NopExec), @@ -252,11 +264,11 @@ func (rs *Repos) processRepoChanges(ctx context.Context, repo types.Repo, r *git switch action.Type { case actionDelete: - if filepath.Base(action.File) != "alr.sh" { + if filepath.Base(action.FilePath) != "alr.sh" { continue } - scriptFl, err := oldCommit.File(action.File) + scriptFl, err := oldCommit.File(action.FilePath) if err != nil { return nil } @@ -277,11 +289,11 @@ func (rs *Repos) processRepoChanges(ctx context.Context, repo types.Repo, r *git return err } case actionUpdate: - if filepath.Base(action.File) != "alr.sh" { - action.File = filepath.Join(filepath.Dir(action.File), "alr.sh") + if filepath.Base(action.FilePath) != "alr.sh" { + action.FilePath = filepath.Join(filepath.Dir(action.FilePath), "alr.sh") } - scriptFl, err := newCommit.File(action.File) + scriptFl, err := newCommit.File(action.FilePath) if err != nil { return nil } diff --git a/pkg/repos/utils_test.go b/pkg/repos/utils_test.go new file mode 100644 index 0000000..c3101c8 --- /dev/null +++ b/pkg/repos/utils_test.go @@ -0,0 +1,168 @@ +// ALR - Any Linux Repository +// Copyright (C) 2025 Евгений Храмов +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package repos + +import ( + "context" + "testing" + "time" + + "github.com/go-git/go-billy/v5/memfs" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/object" + memory "github.com/go-git/go-git/v5/storage/memory" + "github.com/stretchr/testify/assert" +) + +func TestProcessRepoChanges(t *testing.T) { + type testEnv struct { + g *git.Repository + w *git.Worktree + oldCommit *object.Commit + newCommit *object.Commit + } + + type testCase struct { + name string + prepareFunc func(t *testing.T, e *testEnv) + expected []action + } + + commitAll := func(t *testing.T, e *testEnv) *object.Commit { + e.w.AddGlob("*") + hash, err := e.w.Commit("test commit", &git.CommitOptions{ + Author: &object.Signature{ + Name: "Ivan Ivanov", + Email: "author@email.com", + When: time.Now(), + }, + }) + assert.NoError(t, err) + + commit, err := e.g.CommitObject(hash) + assert.NoError(t, err) + + return commit + } + + createFile := func(t *testing.T, e *testEnv, name string, content []byte) { + f, err := e.w.Filesystem.Create(name) + assert.NoError(t, err) + defer f.Close() + + f.Write(content) + } + + removeFile := func(t *testing.T, e *testEnv, name string) { + err := e.w.Filesystem.Remove(name) + assert.NoError(t, err) + } + + for _, tc := range []testCase{ + { + name: "Add package", + prepareFunc: func( + t *testing.T, + e *testEnv, + ) { + createFile(t, e, "alr-repo.toml", []byte("foo-bar-buz")) + e.oldCommit = commitAll(t, e) + + createFile(t, e, "package/alr.sh", []byte("aa")) + e.newCommit = commitAll(t, e) + }, + + expected: []action{ + { + Type: actionUpdate, + FilePath: "package/alr.sh", + }, + }, + }, + { + name: "Update package", + prepareFunc: func( + t *testing.T, + e *testEnv, + ) { + createFile(t, e, "alr-repo.toml", []byte("foo-bar-buz")) + + createFile(t, e, "package/alr.sh", []byte("old content")) + e.oldCommit = commitAll(t, e) + + createFile(t, e, "package/alr.sh", []byte("new content")) + e.newCommit = commitAll(t, e) + }, + + expected: []action{ + { + Type: actionUpdate, + FilePath: "package/alr.sh", + }, + }, + }, + { + name: "Remove package", + prepareFunc: func( + t *testing.T, + e *testEnv, + ) { + createFile(t, e, "alr-repo.toml", []byte("foo-bar-buz")) + commitAll(t, e) + + createFile(t, e, "package/alr.sh", []byte("test")) + e.oldCommit = commitAll(t, e) + + removeFile(t, e, "package/alr.sh") + e.newCommit = commitAll(t, e) + }, + expected: []action{ + { + Type: actionDelete, + FilePath: "package/alr.sh", + }, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + storer := memory.NewStorage() + worktree := memfs.New() + g, err := git.Init(storer, worktree) + assert.NoError(t, err) + w, err := g.Worktree() + assert.NoError(t, err) + + e := &testEnv{ + g, + w, + nil, + nil, + } + + tc.prepareFunc(t, e) + + assert.NotNil(t, e.oldCommit) + assert.NotNil(t, e.newCommit) + + actions, err := parseActionsFromRepoChanges(ctx, g, w, e.oldCommit, e.newCommit) + assert.NoError(t, err) + + assert.Equal(t, tc.expected, actions) + }) + } +}