Skip to content

Commit 339713d

Browse files
committed
Versions for builders and internal package 📦
This adds versions for the builder: v1alpha1 and v1beta1. This also migrate the builder to `internal/builder` while keeping an alias on `test/builder` for user of this package (marking it as deprecated). Signed-off-by: Vincent Demeester <[email protected]>
1 parent 5f4c3c6 commit 339713d

File tree

116 files changed

+8273
-2386
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+8273
-2386
lines changed

‎internal/builder/README.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Builder package for tests
2+
3+
This package holds `Builder` functions that can be used to create struct in
4+
tests with less noise.
5+
6+
One of the most important characteristic of a unit test (and any type of test
7+
really) is **readability**. This means it should be easy to read but most
8+
importantly it should clearly show the intent of the test. The setup (and
9+
cleanup) of the tests should be as small as possible to avoid the noise. Those
10+
builders exists to help with that.
11+
12+
There is currently two versionned builder supported:
13+
- [`v1alpha1`](./v1alpha1)
14+
- [`v1beta1`](./v1beta1)
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Builder package for tests
2+
3+
This package holds `Builder` functions that can be used to create struct in
4+
tests with less noise.
5+
6+
One of the most important characteristic of a unit test (and any type of test
7+
really) is **readability**. This means it should be easy to read but most
8+
importantly it should clearly show the intent of the test. The setup (and
9+
cleanup) of the tests should be as small as possible to avoid the noise. Those
10+
builders exists to help with that.
11+
12+
There is two types of functions defined in that package :
13+
14+
* *Builders*: create and return a struct
15+
* *Modifiers*: return a function
16+
that will operate on a given struct. They can be applied to other
17+
Modifiers or Builders.
18+
19+
Most of the Builder (and Modifier) that accepts Modifiers defines a type
20+
(`TypeOp`) that can be satisfied by existing function in this package, from
21+
other package _or_ inline. An example would be the following.
22+
23+
```go
24+
// Definition
25+
type TaskRunOp func(*v1alpha1.TaskRun)
26+
func TaskRun(name, namespace string, ops ...TaskRunOp) *v1alpha1.TaskRun {
27+
// […]
28+
}
29+
func TaskRunOwnerReference(kind, name string) TaskRunOp {
30+
return func(t *v1alpha1.TaskRun) {
31+
// […]
32+
}
33+
}
34+
// Usage
35+
t := TaskRun("foo", "bar", func(t *v1alpha1.TaskRun){
36+
// Do something with the Task struct
37+
// […]
38+
})
39+
```
40+
41+
The main reason to define the `Op` type, and using it in the methods signatures
42+
is to group Modifier function together. It makes it easier to see what is a
43+
Modifier (or Builder) and on what it operates.
44+
45+
By convention, this package is import with the "tb" as alias. The examples make
46+
that assumption.
47+
48+
## Example
49+
50+
Let's look at a non-exhaustive example.
51+
52+
```go
53+
package builder_test
54+
55+
import (
56+
"fmt"
57+
"testing"
58+
59+
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
60+
tb "github.com/tektoncd/pipeline/internal/builder/v1alpha1"
61+
corev1 "k8s.io/api/core/v1"
62+
)
63+
64+
func MyTest(t *testing.T) {
65+
// You can declare re-usable modifiers
66+
myStep := tb.Step("my-step", "myimage")
67+
// … and use them in a Task definition
68+
myTask := tb.Task("my-task", "namespace", tb.TaskSpec(
69+
tb.Step("simple-step", "myotherimage", tb.Command("/mycmd")),
70+
myStep,
71+
))
72+
// … and another one.
73+
myOtherTask := tb.Task("my-other-task", "namespace",
74+
tb.TaskSpec(myStep,
75+
tb.TaskInputs(tb.InputsResource("workspace", v1alpha1.PipelineResourceTypeGit)),
76+
),
77+
)
78+
myClusterTask := tb.ClusterTask("my-task", tb.ClusterTaskSpec(
79+
tb.Step("simple-step", "myotherimage", tb.Command("/mycmd")),
80+
))
81+
// A simple definition, with a Task reference
82+
myTaskRun := tb.TaskRun("my-taskrun", "namespace", tb.TaskRunSpec(
83+
tb.TaskRunTaskRef("my-task"),
84+
))
85+
// … or a more complex one with inline TaskSpec
86+
myTaskRunWithSpec := tb.TaskRun("my-taskrun-with-spec", "namespace", tb.TaskRunSpec(
87+
tb.TaskRunInputs(
88+
tb.TaskRunInputsParam("myarg", "foo"),
89+
tb.TaskRunInputsResource("workspace", tb.TaskResourceBindingRef("git-resource")),
90+
),
91+
tb.TaskRunTaskSpec(
92+
tb.TaskInputs(
93+
tb.InputsResource("workspace", v1alpha1.PipelineResourceTypeGit),
94+
tb.InputsParam("myarg", tb.ParamDefault("mydefault")),
95+
),
96+
tb.Step("mycontainer", "myimage", tb.Command("/mycmd"),
97+
tb.Args("--my-arg=$(inputs.params.myarg)"),
98+
),
99+
),
100+
))
101+
// Pipeline
102+
pipeline := tb.Pipeline("tomatoes", "namespace",
103+
tb.PipelineSpec(tb.PipelineTask("foo", "banana")),
104+
)
105+
// … and PipelineRun
106+
pipelineRun := tb.PipelineRun("pear", "namespace",
107+
tb.PipelineRunSpec("tomatoes", tb.PipelineRunServiceAccount("inexistent")),
108+
)
109+
// And do something with them
110+
// […]
111+
if _, err := c.PipelineClient.Create(pipeline); err != nil {
112+
t.Fatalf("Failed to create Pipeline `%s`: %s", "tomatoes", err)
113+
}
114+
if _, err := c.PipelineRunClient.Create(pipelineRun); err != nil {
115+
t.Fatalf("Failed to create PipelineRun `%s`: %s", "pear", err)
116+
}
117+
// […]
118+
}
119+
```
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
Copyright 2019 The Tekton Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package builder
18+
19+
import (
20+
corev1 "k8s.io/api/core/v1"
21+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
23+
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
24+
)
25+
26+
// ConditionOp is an operation which modifies a Condition struct.
27+
type ConditionOp func(*v1alpha1.Condition)
28+
29+
// ConditionSpecOp is an operation which modifies a ConditionSpec struct.
30+
type ConditionSpecOp func(spec *v1alpha1.ConditionSpec)
31+
32+
// Condition creates a Condition with default values.
33+
// Any number of Condition modifiers can be passed to transform it.
34+
func Condition(name string, ops ...ConditionOp) *v1alpha1.Condition {
35+
condition := &v1alpha1.Condition{
36+
ObjectMeta: metav1.ObjectMeta{
37+
Name: name,
38+
},
39+
}
40+
for _, op := range ops {
41+
op(condition)
42+
}
43+
return condition
44+
}
45+
46+
// ConditionNamespace sets the namespace on the condition
47+
func ConditionNamespace(namespace string) ConditionOp {
48+
return func(t *v1alpha1.Condition) {
49+
t.ObjectMeta.Namespace = namespace
50+
}
51+
}
52+
53+
// ConditionLabels sets the labels on the condition.
54+
func ConditionLabels(labels map[string]string) ConditionOp {
55+
return func(Condition *v1alpha1.Condition) {
56+
if Condition.ObjectMeta.Labels == nil {
57+
Condition.ObjectMeta.Labels = map[string]string{}
58+
}
59+
for key, value := range labels {
60+
Condition.ObjectMeta.Labels[key] = value
61+
}
62+
}
63+
}
64+
65+
// ConditionSpec creates a ConditionSpec with default values.
66+
// Any number of ConditionSpec modifiers can be passed to transform it.
67+
func ConditionSpec(ops ...ConditionSpecOp) ConditionOp {
68+
return func(Condition *v1alpha1.Condition) {
69+
ConditionSpec := &Condition.Spec
70+
for _, op := range ops {
71+
op(ConditionSpec)
72+
}
73+
Condition.Spec = *ConditionSpec
74+
}
75+
}
76+
77+
// ConditionSpecCheck adds a Container, with the specified name and image, to the Condition Spec Check.
78+
// Any number of Container modifiers can be passed to transform it.
79+
func ConditionSpecCheck(name, image string, ops ...ContainerOp) ConditionSpecOp {
80+
return func(spec *v1alpha1.ConditionSpec) {
81+
c := &corev1.Container{
82+
Name: name,
83+
Image: image,
84+
}
85+
for _, op := range ops {
86+
op(c)
87+
}
88+
spec.Check.Container = *c
89+
}
90+
}
91+
92+
// ConditionDescription sets the description of the condition
93+
func ConditionDescription(desc string) ConditionSpecOp {
94+
return func(spec *v1alpha1.ConditionSpec) {
95+
spec.Description = desc
96+
}
97+
}
98+
99+
// ConditionSpecCheckScript adds a script to the Spec.
100+
func ConditionSpecCheckScript(script string) ConditionSpecOp {
101+
return func(spec *v1alpha1.ConditionSpec) {
102+
spec.Check.Script = script
103+
}
104+
}
105+
106+
// ConditionParamSpec adds a param, with specified name, to the Spec.
107+
// Any number of ParamSpec modifiers can be passed to transform it.
108+
func ConditionParamSpec(name string, pt v1alpha1.ParamType, ops ...ParamSpecOp) ConditionSpecOp {
109+
return func(ps *v1alpha1.ConditionSpec) {
110+
pp := &v1alpha1.ParamSpec{Name: name, Type: pt}
111+
for _, op := range ops {
112+
op(pp)
113+
}
114+
ps.Params = append(ps.Params, *pp)
115+
}
116+
}
117+
118+
// ConditionResource adds a resource with specified name, and type to the ConditionSpec.
119+
func ConditionResource(name string, resourceType v1alpha1.PipelineResourceType) ConditionSpecOp {
120+
return func(spec *v1alpha1.ConditionSpec) {
121+
r := v1alpha1.ResourceDeclaration{
122+
Name: name,
123+
Type: resourceType,
124+
}
125+
spec.Resources = append(spec.Resources, r)
126+
}
127+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
Copyright 2019 The Tekton Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package builder_test
18+
19+
import (
20+
"testing"
21+
22+
"github.com/google/go-cmp/cmp"
23+
corev1 "k8s.io/api/core/v1"
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
26+
tb "github.com/tektoncd/pipeline/internal/builder/v1alpha1"
27+
"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
28+
)
29+
30+
func TestCondition(t *testing.T) {
31+
condition := tb.Condition("cond-name",
32+
tb.ConditionNamespace("foo"),
33+
tb.ConditionLabels(
34+
map[string]string{
35+
"label-1": "label-value-1",
36+
"label-2": "label-value-2",
37+
}),
38+
tb.ConditionSpec(tb.ConditionSpecCheck("", "ubuntu", tb.Command("exit 0")),
39+
tb.ConditionDescription("Test Condition"),
40+
tb.ConditionParamSpec("param-1", v1alpha1.ParamTypeString,
41+
tb.ParamSpecDefault("default"),
42+
tb.ParamSpecDescription("desc")),
43+
tb.ConditionResource("git-resource", v1alpha1.PipelineResourceTypeGit),
44+
tb.ConditionResource("pr", v1alpha1.PipelineResourceTypePullRequest),
45+
),
46+
)
47+
48+
expected := &v1alpha1.Condition{
49+
ObjectMeta: metav1.ObjectMeta{
50+
Name: "cond-name",
51+
Namespace: "foo",
52+
Labels: map[string]string{
53+
"label-1": "label-value-1",
54+
"label-2": "label-value-2",
55+
},
56+
},
57+
Spec: v1alpha1.ConditionSpec{
58+
Check: v1alpha1.Step{
59+
Container: corev1.Container{
60+
Image: "ubuntu",
61+
Command: []string{"exit 0"},
62+
},
63+
},
64+
Description: "Test Condition",
65+
Params: []v1alpha1.ParamSpec{{
66+
Name: "param-1",
67+
Type: v1alpha1.ParamTypeString,
68+
Description: "desc",
69+
Default: &v1alpha1.ArrayOrString{
70+
Type: v1alpha1.ParamTypeString,
71+
StringVal: "default",
72+
}}},
73+
Resources: []v1alpha1.ResourceDeclaration{{
74+
Name: "git-resource",
75+
Type: "git",
76+
}, {
77+
Name: "pr",
78+
Type: "pullRequest",
79+
}},
80+
},
81+
}
82+
83+
if d := cmp.Diff(expected, condition); d != "" {
84+
t.Fatalf("Condition diff -want, +got: %v", d)
85+
}
86+
}
87+
88+
func TestConditionWithScript(t *testing.T) {
89+
condition := tb.Condition("cond-name",
90+
tb.ConditionNamespace("foo"),
91+
tb.ConditionSpec(tb.ConditionSpecCheck("", "ubuntu"),
92+
tb.ConditionSpecCheckScript("ls /tmp"),
93+
),
94+
)
95+
96+
expected := &v1alpha1.Condition{
97+
ObjectMeta: metav1.ObjectMeta{
98+
Name: "cond-name",
99+
Namespace: "foo",
100+
},
101+
Spec: v1alpha1.ConditionSpec{
102+
Check: v1alpha1.Step{
103+
Container: corev1.Container{
104+
Image: "ubuntu",
105+
},
106+
Script: "ls /tmp",
107+
},
108+
},
109+
}
110+
111+
if d := cmp.Diff(expected, condition); d != "" {
112+
t.Fatalf("Condition diff -want, +got: %v", d)
113+
}
114+
115+
}

0 commit comments

Comments
 (0)