diff --git a/src/pkg/retention/policy/action/index_test.go b/src/pkg/retention/policy/action/index_test.go new file mode 100644 index 000000000..6119ef05b --- /dev/null +++ b/src/pkg/retention/policy/action/index_test.go @@ -0,0 +1,90 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package action + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/stretchr/testify/require" + + "github.com/goharbor/harbor/src/pkg/retention/res" + "github.com/stretchr/testify/suite" +) + +// IndexTestSuite tests the rule index +type IndexTestSuite struct { + suite.Suite + + candidates []*res.Candidate +} + +// TestIndexEntry is entry of IndexTestSuite +func TestIndexEntry(t *testing.T) { + suite.Run(t, new(IndexTestSuite)) +} + +// SetupSuite ... +func (suite *IndexTestSuite) SetupSuite() { + Register("fakeAction", newFakePerformer) + + suite.candidates = []*res.Candidate{{ + Namespace: "library", + Repository: "harbor", + Kind: "image", + Tag: "latest", + PushedTime: time.Now().Unix(), + Labels: []string{"L1", "L2"}, + }} +} + +// TestRegister tests register +func (suite *IndexTestSuite) TestGet() { + p, err := Get("fakeAction", nil) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), p) + + results, err := p.Perform(suite.candidates) + require.NoError(suite.T(), err) + assert.Equal(suite.T(), 1, len(results)) + assert.Condition(suite.T(), func() (success bool) { + r := results[0] + success = r.Target != nil && + r.Error == nil && + r.Target.Repository == "harbor" && + r.Target.Tag == "latest" + + return + }) +} + +type fakePerformer struct{} + +// Perform the artifacts +func (p *fakePerformer) Perform(candidates []*res.Candidate) (results []*res.Result, err error) { + for _, c := range candidates { + results = append(results, &res.Result{ + Target: c, + }) + } + + return +} + +func newFakePerformer(params interface{}) Performer { + return &fakePerformer{} +} diff --git a/src/pkg/retention/policy/builder_test.go b/src/pkg/retention/policy/builder_test.go new file mode 100644 index 000000000..155fe5611 --- /dev/null +++ b/src/pkg/retention/policy/builder_test.go @@ -0,0 +1,185 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package policy + +import ( + "testing" + "time" + + "github.com/goharbor/harbor/src/pkg/retention/dep" + + "github.com/goharbor/harbor/src/pkg/retention/res/selectors" + "github.com/pkg/errors" + + "github.com/goharbor/harbor/src/pkg/retention/policy/alg" + "github.com/goharbor/harbor/src/pkg/retention/policy/alg/or" + + "github.com/goharbor/harbor/src/pkg/retention/res/selectors/label" + + "github.com/goharbor/harbor/src/pkg/retention/res/selectors/doublestar" + + "github.com/goharbor/harbor/src/pkg/retention/policy/rule/latestk" + + "github.com/goharbor/harbor/src/pkg/retention/policy/action" + + "github.com/stretchr/testify/assert" + + "github.com/stretchr/testify/require" + + "github.com/goharbor/harbor/src/pkg/retention/policy/rule" + + "github.com/goharbor/harbor/src/pkg/retention/policy/lwp" + + "github.com/goharbor/harbor/src/pkg/retention/res" + + "github.com/stretchr/testify/suite" +) + +// TestBuilderSuite is the suite to test builder +type TestBuilderSuite struct { + suite.Suite + + all []*res.Candidate + oldClient dep.Client +} + +// TestBuilder is the entry of testing TestBuilderSuite +func TestBuilder(t *testing.T) { + suite.Run(t, new(TestBuilderSuite)) +} + +// SetupSuite prepares the testing content if needed +func (suite *TestBuilderSuite) SetupSuite() { + suite.all = []*res.Candidate{ + { + NamespaceID: 1, + Namespace: "library", + Repository: "harbor", + Kind: "image", + Tag: "latest", + PushedTime: time.Now().Unix(), + Labels: []string{"L1", "L2"}, + }, + { + NamespaceID: 1, + Namespace: "library", + Repository: "harbor", + Kind: "image", + Tag: "dev", + PushedTime: time.Now().Unix(), + Labels: []string{"L3"}, + }, + } + + alg.Register(alg.AlgorithmOR, or.New) + selectors.Register(doublestar.Kind, []string{ + doublestar.Matches, + doublestar.Excludes, + doublestar.RepoMatches, + doublestar.RepoExcludes, + doublestar.NSMatches, + doublestar.NSExcludes, + }, doublestar.New) + selectors.Register(label.Kind, []string{label.With, label.Without}, label.New) + action.Register(action.Retain, action.NewRetainAction) + + suite.oldClient = dep.DefaultClient + dep.DefaultClient = &fakeRetentionClient{} +} + +// TearDownSuite ... +func (suite *TestBuilderSuite) TearDownSuite() { + dep.DefaultClient = suite.oldClient +} + +// TestBuild tests the Build function +func (suite *TestBuilderSuite) TestBuild() { + b := &basicBuilder{suite.all} + + params := make(rule.Parameters) + params[latestk.ParameterK] = 10 + + scopeSelectors := make(map[string][]*rule.Selector, 1) + scopeSelectors["repository"] = []*rule.Selector{{ + Kind: doublestar.Kind, + Decoration: doublestar.RepoMatches, + Pattern: "**", + }} + + lm := &lwp.Metadata{ + Algorithm: AlgorithmOR, + Rules: []*rule.Metadata{{ + ID: 1, + Priority: 999, + Action: action.Retain, + Template: latestk.TemplateID, + Parameters: params, + ScopeSelectors: scopeSelectors, + TagSelectors: []*rule.Selector{ + { + Kind: label.Kind, + Decoration: label.With, + Pattern: "L3", + }, + }, + }}, + } + + p, err := b.Build(lm) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), p) + + artifacts := []*res.Candidate{ + { + NamespaceID: 1, + Namespace: "library", + Repository: "harbor", + Kind: "image", + Tag: "dev", + PushedTime: time.Now().Unix(), + Labels: []string{"L3"}, + }, + } + + results, err := p.Process(artifacts) + require.NoError(suite.T(), err) + assert.Equal(suite.T(), 1, len(results)) + assert.Condition(suite.T(), func() (success bool) { + art := results[0] + success = art.Error == nil && + art.Target != nil && + art.Target.Repository == "harbor" && + art.Target.Tag == "latest" + + return + }) +} + +type fakeRetentionClient struct{} + +// GetCandidates ... +func (frc *fakeRetentionClient) GetCandidates(repo *res.Repository) ([]*res.Candidate, error) { + return nil, errors.New("not implemented") +} + +// Delete ... +func (frc *fakeRetentionClient) Delete(candidate *res.Candidate) error { + return nil +} + +// SubmitTask ... +func (frc *fakeRetentionClient) SubmitTask(taskID int64, repository *res.Repository, meta *lwp.Metadata) (string, error) { + return "", errors.New("not implemented") +} diff --git a/src/pkg/retention/policy/rule/index_test.go b/src/pkg/retention/policy/rule/index_test.go new file mode 100644 index 000000000..b1b6822b6 --- /dev/null +++ b/src/pkg/retention/policy/rule/index_test.go @@ -0,0 +1,117 @@ +// Copyright Project Harbor Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package rule + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/stretchr/testify/require" + + "github.com/goharbor/harbor/src/pkg/retention/res" + + "github.com/stretchr/testify/suite" +) + +// IndexTestSuite tests the rule index +type IndexTestSuite struct { + suite.Suite +} + +// TestIndexEntry is entry of IndexTestSuite +func TestIndexEntry(t *testing.T) { + suite.Run(t, new(IndexTestSuite)) +} + +// SetupSuite ... +func (suite *IndexTestSuite) SetupSuite() { + Register(&IndexMeta{ + TemplateID: "fakeEvaluator", + Action: "retain", + Parameters: []*IndexedParam{ + { + Name: "fakeParam", + Type: "int", + Unit: "count", + Required: true, + }, + }, + }, newFakeEvaluator) +} + +// TestRegister tests register +func (suite *IndexTestSuite) TestGet() { + + params := make(Parameters) + params["fakeParam"] = 99 + evaluator, err := Get("fakeEvaluator", params) + require.NoError(suite.T(), err) + require.NotNil(suite.T(), evaluator) + + candidates := []*res.Candidate{{ + Namespace: "library", + Repository: "harbor", + Kind: "image", + Tag: "latest", + PushedTime: time.Now().Unix(), + Labels: []string{"L1", "L2"}, + }} + + res, err := evaluator.Process(candidates) + require.NoError(suite.T(), err) + assert.Equal(suite.T(), 1, len(res)) + assert.Condition(suite.T(), func() bool { + c := res[0] + return c.Repository == "harbor" && c.Tag == "latest" + }) +} + +// TestIndex tests Index +func (suite *IndexTestSuite) TestIndex() { + metas := Index() + require.Equal(suite.T(), 1, len(metas)) + assert.Condition(suite.T(), func() bool { + m := metas[0] + return m.TemplateID == "fakeEvaluator" && + m.Action == "retain" && + len(m.Parameters) > 0 + }) +} + +type fakeEvaluator struct { + i int +} + +// Process rule +func (e *fakeEvaluator) Process(artifacts []*res.Candidate) ([]*res.Candidate, error) { + return artifacts, nil +} + +// Action of the rule +func (e *fakeEvaluator) Action() string { + return "retain" +} + +// newFakeEvaluator is the factory of fakeEvaluator +func newFakeEvaluator(parameters Parameters) Evaluator { + i := 10 + if v, ok := parameters["fakeParam"]; ok { + i = v.(int) + } + + return &fakeEvaluator{i} +}