From 1f478998c68b2d68898943471d1ffc79d505d450 Mon Sep 17 00:00:00 2001 From: Kim Oliver Drechsel Date: Tue, 6 Aug 2024 23:25:41 +0200 Subject: [PATCH] test: add tests for main package --- cmd/doco-cd/main.go | 5 +- cmd/doco-cd/main_test.go | 199 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 cmd/doco-cd/main_test.go diff --git a/cmd/doco-cd/main.go b/cmd/doco-cd/main.go index 897fd72..789b40d 100644 --- a/cmd/doco-cd/main.go +++ b/cmd/doco-cd/main.go @@ -31,7 +31,8 @@ const ( var errMsg string -func handleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter, c *config.AppConfig, p webhook.ParsedPayload, jobID string, dockerCli command.Cli) { +// HandleEvent handles the incoming webhook event +func HandleEvent(ctx context.Context, jobLog *slog.Logger, w http.ResponseWriter, c *config.AppConfig, p webhook.ParsedPayload, jobID string, dockerCli command.Cli) { jobLog = jobLog.With(slog.String("repository", p.FullName)) jobLog.Info("preparing project deployment") @@ -308,7 +309,7 @@ func main() { return } - handleEvent(ctx, jobLog, w, c, payload, jobID, dockerCli) + HandleEvent(ctx, jobLog, w, c, payload, jobID, dockerCli) }) log.Info( diff --git a/cmd/doco-cd/main_test.go b/cmd/doco-cd/main_test.go new file mode 100644 index 0000000..b1cfbf4 --- /dev/null +++ b/cmd/doco-cd/main_test.go @@ -0,0 +1,199 @@ +package main + +import ( + "context" + "fmt" + "log/slog" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/compose" + + "github.com/google/uuid" + "github.com/kimdre/doco-cd/internal/config" + "github.com/kimdre/doco-cd/internal/docker" + "github.com/kimdre/doco-cd/internal/logger" + "github.com/kimdre/doco-cd/internal/webhook" +) + +func TestHandleEvent(t *testing.T) { + testCases := []struct { + name string + payload webhook.ParsedPayload + expectedStatusCode int + expectedResponseBody string + }{ + { + name: "Successful Deployment", + payload: webhook.ParsedPayload{ + Ref: "refs/heads/main", + CommitSHA: "26263c2b44133367927cd1423d8c8457b5befce5", + Name: "doco-cd", + FullName: "kimdre/doco-cd", + CloneURL: "https://github.com/kimdre/doco-cd", + Private: false, + }, + expectedStatusCode: http.StatusCreated, + expectedResponseBody: `{"details":"project deployment successful","job_id":"%s"}%s`, + }, + { + name: "Invalid Reference", + payload: webhook.ParsedPayload{ + Ref: "refs/heads/invalid", + CommitSHA: "26263c2b44133367927cd1423d8c8457b5befce5", + Name: "doco-cd", + FullName: "kimdre/doco-cd", + CloneURL: "https://github.com/kimdre/doco-cd", + Private: false, + }, + expectedStatusCode: http.StatusInternalServerError, + expectedResponseBody: `{"error":"failed to clone repository","details":"couldn't find remote ref \"refs/heads/invalid\"","job_id":"%s"}%s`, + }, + { + name: "Private Repository with Missing Access Token", + payload: webhook.ParsedPayload{ + Ref: "refs/heads/main", + CommitSHA: "26263c2b44133367927cd1423d8c8457b5befce5", + Name: "doco-cd", + FullName: "kimdre/doco-cd", + CloneURL: "https://github.com/kimdre/doco-cd", + Private: true, + }, + expectedStatusCode: http.StatusInternalServerError, + expectedResponseBody: `{"error":"missing access token for private repository","job_id":"%s"}%s`, + }, + { + name: "Private Repository with Missing Access Token", + payload: webhook.ParsedPayload{ + Ref: "refs/heads/main", + CommitSHA: "26263c2b44133367927cd1423d8c8457b5befce5", + Name: "doco-cd", + FullName: "kimdre/doco-cd", + CloneURL: "https://github.com/kimdre/doco-cd", + Private: true, + }, + expectedStatusCode: http.StatusInternalServerError, + expectedResponseBody: `{"error":"missing access token for private repository","job_id":"%s"}%s`, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + appConfig, _ := config.GetAppConfig() + + log := logger.New(12) + jobID := uuid.Must(uuid.NewRandom()).String() + jobLog := log.With(slog.String("job_id", jobID)) + + ctx := context.Background() + + dockerCli, err := docker.CreateDockerCli() + if err != nil { + if tc.expectedStatusCode == http.StatusInternalServerError { + return + } + + t.Fatalf("Failed to create docker client: %v", err) + } + + t.Cleanup(func() { + err = dockerCli.Client().Close() + if err != nil { + return + } + }) + + t.Cleanup(func() { + service := compose.NewComposeService(dockerCli) + + downOpts := api.DownOptions{ + RemoveOrphans: true, + Images: "all", + Volumes: true, + } + + t.Log("Remove test container") + + err = service.Down(ctx, tc.payload.Name, downOpts) + if err != nil { + t.Fatal(err) + } + }) + + err = docker.VerifySocketConnection(appConfig.DockerAPIVersion) + if err != nil { + t.Fatalf("Failed to verify docker socket connection: %v", err) + } + + rr := httptest.NewRecorder() + + HandleEvent( + ctx, + jobLog, + rr, + appConfig, + tc.payload, + jobID, + dockerCli, + ) + + if status := rr.Code; status != tc.expectedStatusCode { + t.Errorf("handler returned wrong status code: got %v want %v", + status, tc.expectedStatusCode) + } + + expectedReturnMessage := fmt.Sprintf(tc.expectedResponseBody, jobID, "\n") + if rr.Body.String() != expectedReturnMessage { + t.Errorf("handler returned unexpected body: got '%v' want '%v'", + rr.Body.String(), expectedReturnMessage) + } + }) + } +} + +func TestJSONResponse(t *testing.T) { + rr := httptest.NewRecorder() + + jobId := uuid.Must(uuid.NewRandom()).String() + + JSONResponse(rr, "this is a test", jobId, http.StatusOK) + + if rr.Code != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + rr.Code, http.StatusOK) + } + + expectedReturnMessage := fmt.Sprintf(`{"details":"this is a test","job_id":"%s"}%s`, jobId, "\n") + if rr.Body.String() != expectedReturnMessage { + t.Errorf("handler returned unexpected body: got '%v' want '%v'", + rr.Body.String(), expectedReturnMessage) + } +} + +func TestJSONError(t *testing.T) { + rr := httptest.NewRecorder() + + jobId := uuid.Must(uuid.NewRandom()).String() + + JSONError(rr, "this is a error", "this is a detail", jobId, http.StatusInternalServerError) + + if rr.Code != http.StatusInternalServerError { + t.Errorf("handler returned wrong status code: got %v want %v", + rr.Code, http.StatusInternalServerError) + } + + expectedReturnMessage := fmt.Sprintf(`{"error":"this is a error","details":"this is a detail","job_id":"%s"}%s`, jobId, "\n") + if rr.Body.String() != expectedReturnMessage { + t.Errorf("handler returned unexpected body: got '%v' want '%v'", + rr.Body.String(), expectedReturnMessage) + } +} + +// Test main function +func TestMain(m *testing.M) { + // Run the tests + os.Exit(m.Run()) +}