138 lines
5.2 KiB
Go
138 lines
5.2 KiB
Go
package e2e
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type fakeCommand struct {
|
|
output func(string, []string) ([]byte, error)
|
|
calls [][]string
|
|
}
|
|
|
|
func (f *fakeCommand) Output(_ context.Context, name string, args ...string) ([]byte, error) {
|
|
f.calls = append(f.calls, append([]string{name}, args...))
|
|
return f.output(name, args)
|
|
}
|
|
|
|
type fakeHTTP struct {
|
|
do func(*http.Request) (*http.Response, error)
|
|
}
|
|
|
|
func (f fakeHTTP) Do(request *http.Request) (*http.Response, error) { return f.do(request) }
|
|
|
|
func response(status int, body string) *http.Response {
|
|
return &http.Response{StatusCode: status, Body: io.NopCloser(strings.NewReader(body)), Header: make(http.Header)}
|
|
}
|
|
|
|
func testOptions() Options {
|
|
return Options{
|
|
Kubeconfig: "/run/secrets/kubeconfig",
|
|
FluxKustomizations: []string{"flux-system/tekton"},
|
|
ExternalSecret: "tekton-pipelines/forgejo-webhook",
|
|
PipelineRun: "tekton-pipelines/delivery-1",
|
|
PreviewURL: "https://preview.example.test/",
|
|
PreviewSentinel: "maidn-e2e-ok",
|
|
PromotionPullsURL: "https://git.example.test/api/v1/repos/Maidn/manifests/pulls",
|
|
PromotionOwner: "Maidn",
|
|
PromotionHead: "maidn/promotion-app-0123456789abcdef0123456789abcdef01234567",
|
|
PromotionToken: "test-token",
|
|
Timeout: time.Second,
|
|
Interval: time.Millisecond,
|
|
}
|
|
}
|
|
|
|
func TestRunUsesReadOnlyBoundariesAndRedactsResponses(t *testing.T) {
|
|
kubectl := &fakeCommand{output: func(_ string, args []string) ([]byte, error) {
|
|
if strings.Contains(strings.Join(args, " "), "pipelineruns.tekton.dev") {
|
|
return []byte(`{"status":{"conditions":[{"type":"Succeeded","status":"True"}]}}`), nil
|
|
}
|
|
return []byte(`{"status":{"conditions":[{"type":"Ready","status":"True"}]},"data":"secret-value"}`), nil
|
|
}}
|
|
http := fakeHTTP{do: func(request *http.Request) (*http.Response, error) {
|
|
if strings.Contains(request.URL.Path, "/pulls") {
|
|
if request.Header.Get("Authorization") != "token test-token" {
|
|
t.Fatal("promotion request did not use the supplied token")
|
|
}
|
|
if got := request.URL.Query().Get("head"); got != "Maidn:maidn/promotion-app-0123456789abcdef0123456789abcdef01234567" {
|
|
t.Fatalf("promotion head = %q", got)
|
|
}
|
|
return response(http.StatusOK, `[{"state":"open","body":"secret-value"}]`), nil
|
|
}
|
|
return response(http.StatusOK, "maidn-e2e-ok secret-value"), nil
|
|
}}
|
|
|
|
result, err := (Runner{Kubectl: kubectl, HTTP: http}).Run(context.Background(), testOptions())
|
|
if err != nil || !result.Passed || len(result.Checks) != 5 {
|
|
t.Fatalf("Run() = %#v, %v", result, err)
|
|
}
|
|
encoded, err := json.Marshal(result)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(encoded), "secret-value") || strings.Contains(string(encoded), "test-token") {
|
|
t.Fatalf("result exposed response data: %s", encoded)
|
|
}
|
|
for _, call := range kubectl.calls {
|
|
joined := strings.Join(call, " ")
|
|
if !strings.Contains(joined, " get ") || strings.Contains(joined, "apply") || strings.Contains(joined, "reconcile") {
|
|
t.Fatalf("unexpected kubectl invocation: %q", joined)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRunReportsTerminalPipelineFailureWithoutWaiting(t *testing.T) {
|
|
kubectl := &fakeCommand{output: func(_ string, args []string) ([]byte, error) {
|
|
if strings.Contains(strings.Join(args, " "), "pipelineruns.tekton.dev") {
|
|
return []byte(`{"status":{"conditions":[{"type":"Succeeded","status":"False"}]}}`), nil
|
|
}
|
|
return []byte(`{"status":{"conditions":[{"type":"Ready","status":"True"}]}}`), nil
|
|
}}
|
|
http := fakeHTTP{do: func(request *http.Request) (*http.Response, error) {
|
|
if strings.Contains(request.URL.Path, "/pulls") {
|
|
return response(http.StatusOK, `[{"state":"open"}]`), nil
|
|
}
|
|
return response(http.StatusOK, "maidn-e2e-ok"), nil
|
|
}}
|
|
|
|
result, err := (Runner{Kubectl: kubectl, HTTP: http}).Run(context.Background(), testOptions())
|
|
if err != nil || result.Passed || result.Checks[2].Detail != "failed" {
|
|
t.Fatalf("Run() = %#v, %v", result, err)
|
|
}
|
|
}
|
|
|
|
func TestRunTimesOutWhenAReadinessConditionNeverArrives(t *testing.T) {
|
|
kubectl := &fakeCommand{output: func(_ string, args []string) ([]byte, error) {
|
|
if strings.Contains(strings.Join(args, " "), "externalsecrets.external-secrets.io") {
|
|
return []byte(`{"status":{"conditions":[{"type":"Ready","status":"False"}]}}`), nil
|
|
}
|
|
if strings.Contains(strings.Join(args, " "), "pipelineruns.tekton.dev") {
|
|
return []byte(`{"status":{"conditions":[{"type":"Succeeded","status":"True"}]}}`), nil
|
|
}
|
|
return []byte(`{"status":{"conditions":[{"type":"Ready","status":"True"}]}}`), nil
|
|
}}
|
|
http := fakeHTTP{do: func(request *http.Request) (*http.Response, error) {
|
|
if strings.Contains(request.URL.Path, "/pulls") {
|
|
return response(http.StatusOK, `[{"state":"open"}]`), nil
|
|
}
|
|
return response(http.StatusOK, "maidn-e2e-ok"), nil
|
|
}}
|
|
options := testOptions()
|
|
options.Timeout, options.Interval = 5*time.Millisecond, time.Millisecond
|
|
result, err := (Runner{Kubectl: kubectl, HTTP: http}).Run(context.Background(), options)
|
|
if err != nil || result.Passed || result.Checks[1].Detail != "timed_out" {
|
|
t.Fatalf("Run() = %#v, %v", result, err)
|
|
}
|
|
}
|
|
|
|
func TestReadTokenRejectsAmbiguousReferences(t *testing.T) {
|
|
if _, err := ReadToken("PROMOTION_TOKEN", "token.txt"); err == nil {
|
|
t.Fatal("ReadToken accepted two token references")
|
|
}
|
|
}
|