Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ RUN CGO_ENABLED=0 GOOS=linux go build -o /app ./cmd/worker

FROM ubuntu:noble

RUN apt-get update && apt-get install lsof iputils-ping -y

COPY --from=build /app /app

EXPOSE 8080
Expand Down
3 changes: 3 additions & 0 deletions tests/helper/stdout.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package helper

import (
"fmt"
"io"
"os"
)
Expand All @@ -17,6 +18,8 @@ func CaptureStdout(f func()) string {

f()

fmt.Println() // to flush data

w.Close()
b, _ := io.ReadAll(r)

Expand Down
40 changes: 40 additions & 0 deletions tests/worker/start_task_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package worker

import (
"context"
"dirigeant/task"
"dirigeant/tests/helper"
"dirigeant/worker"
Expand All @@ -9,6 +10,7 @@ import (
"net/http/httptest"
"sync"
"testing"
"time"

"github.com/google/uuid"
"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -105,3 +107,41 @@ func TestStartTask__AllButOneRequestsShouldFailIfCreatingTheSameTaskSimultaneous
assert.Equal(t, 1, len(api.Worker.Tasks), "Tasks map should contain 1 task")
assert.NotNil(t, api.Worker.Tasks[testTask.ID], "Persisted task ID should match the one from request")
}

func TestStartTask__ShouldHandleClientClosedRequest(t *testing.T) {
api := &worker.Api{
Worker: &worker.Worker{
Tasks: make(map[uuid.UUID]*task.Task),
},
}
testTask := helper.PingTask("ping-task", "127.0.0.1")
ctx, cancel := context.WithCancel(context.TODO())

// 1 - Create a task
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()

createRequest := helper.NewTaskPostRequest(testTask).WithContext(ctx)
createResponseRecorder := httptest.NewRecorder()

stdout := helper.CaptureStdout(func() {
api.HandleCreateTask(createResponseRecorder, createRequest)
})

assert.Equal(t, 499, createResponseRecorder.Code, "Response status code should be 499 Client Closed Request")
assert.Equal(t, "Error when executing the task: client closed request", createResponseRecorder.Body.String(), "Response body should contain error message")
assert.NotEmpty(t, stdout, "Task logs shouldn't be empty")
assert.Empty(t, api.Worker.Tasks, "Tasks map should be empty")
}()

time.Sleep(1 * time.Second)

assert.Equal(t, 1, len(api.Worker.Tasks), "Tasks map should contain 1 task")

// 2 - Cancel a request
cancel()

wg.Wait()
}
34 changes: 25 additions & 9 deletions worker/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,34 @@ func (a *Api) HandleCreateTask(w http.ResponseWriter, r *http.Request) {
return
}

if err := a.Worker.StartTask(t); err != nil {
if errors.Is(err, task.ErrAlreadyExists) {
w.WriteHeader(http.StatusConflict)
} else {
w.WriteHeader(http.StatusInternalServerError)
errCh := make(chan error)
defer close(errCh)
go func() {
errCh <- a.Worker.StartTask(t)
}()

select {
case <-r.Context().Done():
a.Worker.StopTask(t.ID)

<-errCh

w.WriteHeader(499) // client closed request
fmt.Fprint(w, "Error when executing the task: client closed request")
case err := <-errCh:
if err != nil {
if errors.Is(err, task.ErrAlreadyExists) {
w.WriteHeader(http.StatusConflict)
} else {
w.WriteHeader(http.StatusInternalServerError)
}

fmt.Fprintf(w, "Error when executing the task: %v", err)
return
}

fmt.Fprintf(w, "Error when executing the task: %v", err)
return
w.WriteHeader(http.StatusCreated)
}

w.WriteHeader(http.StatusCreated)
}

func (a *Api) HandleDeleteTask(w http.ResponseWriter, r *http.Request) {
Expand Down
Loading