Repository: openshift-pipelines/pipelines-as-code
Pull request #131 (2021-06-11 09:00:39)

A bunch of miscies
- remove tkn reference names and rename it  to pac
- remove reference to tekton.yaml
- improve time test
- Add tests for annotation installing from hub



Patch:
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index d6456956..00000000
--- a/LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   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.
diff --git a/Makefile b/Makefile
index 20a09f05..76d1f2b1 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ ifneq ($(FLAGS),)
 	LDFLAGS := -ldflags "$(FLAGS)"
 endif
 
-all: bin/tkn test
+all: bin/pipelines-as-code test
 
 FORCE:
 
diff --git a/README.md b/README.md
index ce0de6b4..335c2bc8 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[![Container Repository on Quay](https://quay.io/repository/openshift-pipeline/pipelines-as-code/status "Container Repository on Quay")](https://quay.io/repository/openshift-pipeline/pipelines-as-code) [![codecov](https://codecov.io/gh/openshift-pipelines/pipelines-as-code/branch/main/graph/badge.svg)](https://codecov.io/gh/openshift-pipelines/pipelines-as-code) [![Go Report Card](https://goreportcard.com/badge/google/ko)](https://goreportcard.com/report/openshift-pipelines/pipelines-as-code)
+[![Container Repository on Quay](https://quay.io/repository/openshift-pipeline/pipelines-as-code/status "Container Repository on Quay")](https://quay.io/repository/openshift-pipeline/pipelines-as-code) [![codecov](https://codecov.io/gh/openshift-pipelines/pipelines-as-code/branch/main/graph/badge.svg)](https://codecov.io/gh/openshift-pipelines/pipelines-as-code)
 
 # Pipelines as Code
 
diff --git a/cmd/pipelines-as-code/main.go b/cmd/pipelines-as-code/main.go
index e34f7bfb..790512ba 100644
--- a/cmd/pipelines-as-code/main.go
+++ b/cmd/pipelines-as-code/main.go
@@ -9,16 +9,16 @@ import (
 
 func main() {
 	tp := &cli.PacParams{}
-	tkn := cmd.Root(tp)
+	pac := cmd.Root(tp)
 
-	cmd, _, err := tkn.Find(os.Args[1:])
+	cmd, _, err := pac.Find(os.Args[1:])
 	// default cmd if no cmd is given
 	if err != nil || cmd.Use == "" {
 		args := append([]string{"run"}, os.Args[1:]...)
-		tkn.SetArgs(args)
+		pac.SetArgs(args)
 	}
 
-	if err := tkn.Execute(); err != nil {
+	if err := pac.Execute(); err != nil {
 		os.Exit(1)
 	}
 }
diff --git a/pkg/hub/get.go b/pkg/hub/get.go
index f93613f1..f7de828d 100644
--- a/pkg/hub/get.go
+++ b/pkg/hub/get.go
@@ -51,8 +51,8 @@ func getSpecificVersion(ctx context.Context, cli *cli.Clients, task string) (str
 }
 
 func getLatestVersion(ctx context.Context, cli *cli.Clients, task string) (string, error) {
-	data, err := getURL(ctx, cli, fmt.Sprintf("%s/resource/%s/task/%s", hubBaseURL, tektonCatalogHubName, task))
 	hr := new(hubResource)
+	data, err := getURL(ctx, cli, fmt.Sprintf("%s/resource/%s/task/%s", hubBaseURL, tektonCatalogHubName, task))
 	if err != nil {
 		return "", err
 	}
diff --git a/pkg/pipelineascode/status.go b/pkg/pipelineascode/status.go
index ef041410..55bdb404 100644
--- a/pkg/pipelineascode/status.go
+++ b/pkg/pipelineascode/status.go
@@ -32,7 +32,7 @@ const taskStatustmpl = `
 {{- end }}
 </table>`
 
-const NAStr = "---"
+const naStr = "---"
 
 type tkr struct {
 	TaskrunName string
@@ -86,7 +86,7 @@ func pipelineRunStatus(pr *tektonv1beta1.PipelineRun) string {
 func ConditionEmoji(c knative1.Conditions) string {
 	var status string
 	if len(c) == 0 {
-		return NAStr
+		return naStr
 	}
 
 	// TODO: there is other weird errors we need to handle.
diff --git a/pkg/pipelineascode/time.go b/pkg/pipelineascode/time.go
index 10cedde3..916cc474 100644
--- a/pkg/pipelineascode/time.go
+++ b/pkg/pipelineascode/time.go
@@ -22,7 +22,7 @@ import (
 
 func Age(t *metav1.Time, c clockwork.Clock) string {
 	if t.IsZero() {
-		return NAStr
+		return naStr
 	}
 
 	dur := c.Since(t.Time)
@@ -31,7 +31,7 @@ func Age(t *metav1.Time, c clockwork.Clock) string {
 
 func Duration(t1, t2 *metav1.Time) string {
 	if t1.IsZero() || t2.IsZero() {
-		return NAStr
+		return naStr
 	}
 
 	dur := t2.Time.Sub(t1.Time)
@@ -40,7 +40,7 @@ func Duration(t1, t2 *metav1.Time) string {
 
 func Timeout(t *metav1.Duration) string {
 	if t == nil {
-		return NAStr
+		return naStr
 	}
 
 	return durafmt.Parse(t.Duration).String()
diff --git a/pkg/webvcs/github.go b/pkg/webvcs/github.go
index acc4c0e4..a86cadaa 100644
--- a/pkg/webvcs/github.go
+++ b/pkg/webvcs/github.go
@@ -339,8 +339,8 @@ func (v GithubVCS) ConcatAllYamlFiles(ctx context.Context, objects []*github.Rep
 	var allTemplates string
 
 	for _, value := range objects {
-		if value.GetName() != "tekton.yaml" && (strings.HasSuffix(value.GetName(), ".yaml") ||
-			strings.HasSuffix(value.GetName(), ".yml")) {
+		if strings.HasSuffix(value.GetName(), ".yaml") ||
+			strings.HasSuffix(value.GetName(), ".yml") {
 			data, err := v.GetObject(ctx, value.GetSHA(), runinfo)
 			if err != nil {
 				return "", err


Test patch:
diff --git a/pkg/config/annotation_tasks_install_test.go b/pkg/config/annotation_tasks_install_test.go
index 59a34fba..e3d2ec8f 100644
--- a/pkg/config/annotation_tasks_install_test.go
+++ b/pkg/config/annotation_tasks_install_test.go
@@ -65,48 +65,59 @@ spec:
 	})
 
 	tests := []struct {
-		name                string
-		runinfo             *webvcs.RunInfo
-		annotations         map[string]string
-		wantErr             string
-		remoteURLDataReturn string
-		remoteURL           string
-		gotTaskName         string
+		name        string
+		runinfo     *webvcs.RunInfo
+		annotations map[string]string
+		wantErr     string
+		gotTaskName string
+		remoteURLS  map[string]map[string]string
 	}{
 		{
 			name: "test-annotations-error-remote-http-not-k8",
 			annotations: map[string]string{
 				pipelinesascode.GroupName + "/task": "[http://remote.task]",
 			},
-			remoteURL: "http://remote.task",
-			wantErr:   "not looking like a kubernetes resource",
+			remoteURLS: map[string]map[string]string{
+				"https://remote.task": {
+					"body": "",
+					"code": "200",
+				},
+			},
+			wantErr: "not looking like a kubernetes resource",
 		},
 		{
 			name: "test-annotations-remote-http",
 			annotations: map[string]string{
 				pipelinesascode.GroupName + "/task": "[http://remote.task]",
 			},
-			gotTaskName:         "task",
-			remoteURL:           "http://remote.task",
-			remoteURLDataReturn: simpletask,
+			gotTaskName: "task",
+			remoteURLS: map[string]map[string]string{
+				"http://remote.task": {
+					"body": simpletask,
+					"code": "200",
+				},
+			},
 		},
 		{
 			name: "test-annotations-remote-https",
 			annotations: map[string]string{
 				pipelinesascode.GroupName + "/task": "[https://remote.task]",
 			},
-			gotTaskName:         "task",
-			remoteURL:           "https://remote.task",
-			remoteURLDataReturn: simpletask,
+			gotTaskName: "task",
+			remoteURLS: map[string]map[string]string{
+				"https://remote.task": {
+					"body": simpletask,
+					"code": "200",
+				},
+			},
 		},
 		{
 			name: "test-annotations-inside-repo",
 			annotations: map[string]string{
 				pipelinesascode.GroupName + "/task": "[be/healthy]",
 			},
-			gotTaskName:         "task",
-			remoteURLDataReturn: simpletask,
-			runinfo:             &webvcs.RunInfo{},
+			gotTaskName: "task",
+			runinfo:     &webvcs.RunInfo{},
 		},
 		{
 			name: "test-annotations-remote-http-skipping-notmatching",
@@ -114,9 +125,13 @@ spec:
 				pipelinesascode.GroupName + "/task":  "[http://remote.task]",
 				pipelinesascode.GroupName + "/taskA": "[http://other.task]", // That's wrong this would be skipped
 			},
-			gotTaskName:         "task",
-			remoteURL:           "http://remote.task",
-			remoteURLDataReturn: simpletask,
+			gotTaskName: "task",
+			remoteURLS: map[string]map[string]string{
+				"http://remote.task": {
+					"body": simpletask,
+					"code": "200",
+				},
+			},
 		},
 		{
 			name: "test-annotations-remote-http-bad-annotation",
@@ -133,15 +148,44 @@ spec:
 			runinfo: &webvcs.RunInfo{},
 			wantErr: "404",
 		},
+		{
+			name:        "test-get-from-hub-latest",
+			gotTaskName: "task",
+			annotations: map[string]string{
+				pipelinesascode.GroupName + "/task": "[chmouzie]",
+			},
+			remoteURLS: map[string]map[string]string{
+				"https://api.hub.tekton.dev/v1/resource/tekton/task/chmouzie": {
+					"body": `{"data": {"LatestVersion": {"RawURL": "http://simple.task"}}}`,
+					"code": "200",
+				},
+				"http://simple.task": {
+					"body": simpletask,
+					"code": "200",
+				},
+			},
+		},
+		{
+			name:        "test-get-from-hub-specific-version",
+			gotTaskName: "task",
+			annotations: map[string]string{
+				pipelinesascode.GroupName + "/task": "[chmouzie:0.2]",
+			},
+			remoteURLS: map[string]map[string]string{
+				"https://api.hub.tekton.dev/v1/resource/tekton/task/chmouzie/0.2": {
+					"body": `{"data": {"RawURL": "http://simple.task"}}`,
+					"code": "200",
+				},
+				"http://simple.task": {
+					"body": simpletask,
+					"code": "200",
+				},
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			httpTestClient := httptesthelper.MakeHTTPTestClient(t, map[string]map[string]string{
-				tt.remoteURL: {
-					"body": tt.remoteURLDataReturn,
-					"code": "200",
-				},
-			})
+			httpTestClient := httptesthelper.MakeHTTPTestClient(t, tt.remoteURLS)
 			cs := &cli.Clients{
 				HTTPClient: *httpTestClient,
 				GithubClient: webvcs.GithubVCS{
diff --git a/pkg/pipelineascode/pipelineascode_test.go b/pkg/pipelineascode/pipelineascode_test.go
index 5e633a4d..42413e19 100644
--- a/pkg/pipelineascode/pipelineascode_test.go
+++ b/pkg/pipelineascode/pipelineascode_test.go
@@ -35,69 +35,6 @@ func replyString(mux *http.ServeMux, url, body string) {
 	})
 }
 
-func TestRunDeniedFromForcedNamespace(t *testing.T) {
-	t.Skip("TODO: Disabled until we reimplement this as annotation")
-	ctx, _ := rtesting.SetupFakeContext(t)
-	fakeclient, mux, _, teardown := ghtesthelper.SetupGH()
-	defer teardown()
-	defaultBranch := "default"
-	forcedNamespace := "laotronamspace"
-	installedNamespace := "nomespace"
-	runinfo := &webvcs.RunInfo{
-		SHA:           "sha",
-		Owner:         "organizatione",
-		Repository:    "repo",
-		URL:           "https://service/documentation",
-		HeadBranch:    "press",
-		BaseBranch:    "main",
-		DefaultBranch: defaultBranch,
-		Sender:        "carlos_valderama",
-	}
-	replyString(mux,
-		fmt.Sprintf("/repos/%s/%s/check-runs", runinfo.Owner, runinfo.Repository),
-		`{"id": 26}`)
-
-	mux.HandleFunc(fmt.Sprintf("/repos/%s/%s/contents/.tekton/tekton.yaml", runinfo.Owner, runinfo.Repository),
-		func(w http.ResponseWriter, r *http.Request) {
-			if r.URL.Query().Get("ref") != defaultBranch {
-				fmt.Fprint(w, `{}`)
-			} else {
-				fmt.Fprint(w, `{"sha": "shaofmaintektonyaml"}`)
-			}
-		})
-
-	forcedNamespaceContent := base64.StdEncoding.EncodeToString([]byte("namespace: " + forcedNamespace + "\n"))
-	mux.HandleFunc(fmt.Sprintf("/repos/%s/%s/git/blobs/shaofmaintektonyaml", runinfo.Owner, runinfo.Repository),
-		func(w http.ResponseWriter, r *http.Request) {
-			fmt.Fprintf(w, `{"content": "%s"}`, forcedNamespaceContent)
-		})
-
-	mux.HandleFunc("/orgs/"+runinfo.Owner+"/public_members", func(rw http.ResponseWriter, r *http.Request) {
-		fmt.Fprintf(rw, `[{"login": "%s"}]`, runinfo.Sender)
-	})
-
-	gcvs := webvcs.GithubVCS{
-		Client: fakeclient,
-	}
-	datas := testclient.Data{
-		Repositories: []*v1alpha1.Repository{
-			repository.NewRepo("repo", runinfo.URL, runinfo.BaseBranch, installedNamespace, installedNamespace, "pull_request"),
-		},
-	}
-	stdata, _ := testclient.SeedTestData(t, ctx, datas)
-	cs := &cli.Clients{
-		GithubClient:   gcvs,
-		PipelineAsCode: stdata.PipelineAsCode,
-	}
-	k8int := kitesthelper.KinterfaceTest{}
-
-	err := Run(ctx, cs, &k8int, runinfo)
-
-	assert.Error(t, err,
-		fmt.Sprintf("repo CR matches but should be installed in %s as configured from tekton.yaml on the main branch",
-			forcedNamespace))
-}
-
 func testSetupTektonDir(mux *http.ServeMux, runinfo *webvcs.RunInfo, directory string) {
 	var tektonDirContent string
 	_ = filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
diff --git a/pkg/pipelineascode/time_test.go b/pkg/pipelineascode/time_test.go
index 750c42ee..04e36149 100644
--- a/pkg/pipelineascode/time_test.go
+++ b/pkg/pipelineascode/time_test.go
@@ -18,6 +18,7 @@ import (
 	"testing"
 	"time"
 
+	"github.com/jonboulle/clockwork"
 	"gotest.tools/v3/assert"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
@@ -31,5 +32,26 @@ func TestTimeout(t *testing.T) {
 	assert.Equal(t, str, "5 minutes")
 
 	str = Timeout(nil) // Timeout is not defined
-	assert.Equal(t, str, NAStr)
+	assert.Equal(t, str, naStr)
+}
+
+func TestAge(t *testing.T) {
+	clock := clockwork.NewFakeClock()
+	assert.Equal(t, Age(&metav1.Time{}, clock), naStr)
+
+	t1 := &metav1.Time{
+		Time: clock.Now().Add(-5 * time.Minute),
+	}
+	assert.Equal(t, Age(t1, clock), "5 minutes ago")
+}
+
+func TestDuration(t *testing.T) {
+	assert.Equal(t, Duration(&metav1.Time{}, &metav1.Time{}), naStr)
+	clock := clockwork.NewFakeClock()
+
+	assert.Equal(t, Duration(&metav1.Time{
+		Time: clock.Now(),
+	}, &metav1.Time{
+		Time: clock.Now().Add(5 * time.Minute),
+	}), "5 minutes")
 }
diff --git a/pkg/webvcs/github_test.go b/pkg/webvcs/github_test.go
index 5a602a2a..2bb1f647 100644
--- a/pkg/webvcs/github_test.go
+++ b/pkg/webvcs/github_test.go
@@ -89,13 +89,7 @@ func setupFakesURLS() (client GithubVCS, teardown func()) {
 				  "path": ".tekton/run.yaml",
 				  "sha": "runyaml",
 				  "type": "file"
-				},
-				{
-				  "name": "tekton.yaml",
-				  "path": ".tekton/tekton.yaml",
-				  "sha": "tektonyaml",
-				  "type": "file"
-			 }]`)
+				}]`)
 	})
 	mux.HandleFunc("/repos/throw/error/contents/.tekton", func(w http.ResponseWriter, r *http.Request) {
 		_, _ = fmt.Fprint(w, "ERRROR")