Pular para o conteúdo principal

victorstein.dev

80/100 Dias de Golang - Mini Servidor de CI/CD – Parte 4 - Adicionando testes ao repositório!

Table of Contents

# Mini Servidor de CI/CD – Parte 4: Adicionando testes ao repositório!

Finalizamos a implementação da API e a execução dos pipelines. Agora é hora de garantir que tudo isso funcione como esperado com testes automatizados. Para fazer os testes vamos instalar a go get github.com/stretchr/testify e criar nosso arquivo: internal/api/test/handlers_test.go

Vamos iniciar criando um setup para nossos testes. Para isso vamos criar um banco em memória.

func setupTestRouter() *gin.Engine {
	database, _ := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	db.DB = database
	db.DB.AutoMigrate(&models.Pipeline{}, &models.Step{}, &models.Execution{})

	r := gin.Default()
	api.RegisterRoutes(r)
	return r
}

Primeiro vamos testar a criação de um pipeline. Devemos esperar desse request um retorno de sucesso.

func TestCreatePipeline(t *testing.T) {
	router := setupTestRouter()

	payload := map[string]interface{}{
		"name": "Pipeline Teste",
		"repo": "https://github.com/exemplo/teste",
		"steps": []map[string]interface{}{
			{"order": 1, "command": "echo Testando"},
		},
	}
	body, _ := json.Marshal(payload)

	req, _ := http.NewRequest("POST", "/pipelines", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusCreated, w.Code)
}

Agora vamos testar a listagem dos pipelines, vamos inserir um dado manualmente no banco de dados.

func TestGetAllPipelines(t *testing.T) {
	router := setupTestRouter()

	db.DB.Create(&models.Pipeline{
		Name: "Teste",
		Repo: "https://github.com/x/y",
		Steps: []models.Step{
			{Order: 1, Command: "echo step"},
		},
	})

	req, _ := http.NewRequest("GET", "/pipelines", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Contains(t, w.Body.String(), "Teste")
}

E agora adicionar um teste de sucesso para a execução de um pipeline e um teste de falha:

func TestRunPipelineSuccess(t *testing.T) {
	router := setupTestRouter()

	pipeline := models.Pipeline{
		Name: "Runner Test",
		Repo: "https://fake.repo",
		Steps: []models.Step{
			{Order: 1, Command: "echo sucesso"},
		},
	}
	db.DB.Create(&pipeline)

	url := "/pipelines/run/" + strconv.Itoa(int(pipeline.ID))
	req, _ := http.NewRequest("POST", url, nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Contains(t, w.Body.String(), "sucesso")
}

func TestRunPipelineFailure(t *testing.T) {
	router := setupTestRouter()

	pipeline := models.Pipeline{
		Name: "Runner Falha",
		Repo: "https://fake.repo",
		Steps: []models.Step{
			{Order: 1, Command: "exit 1"},
		},
	}
	db.DB.Create(&pipeline)

	url := "/pipelines/run/" + strconv.Itoa(int(pipeline.ID))
	req, _ := http.NewRequest("POST", url, nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusInternalServerError, w.Code)
	assert.Contains(t, w.Body.String(), "Erro na execução")
}

O arquivo completo fica da seguinte forma:

package test

import (
	"bytes"
	"cicd-server/internal/api"
	"cicd-server/internal/db"
	"cicd-server/internal/models"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strconv"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func setupTestRouter() *gin.Engine {
	// Banco em memória para testes
	database, _ := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	db.DB = database
	db.DB.AutoMigrate(&models.Pipeline{}, &models.Step{}, &models.Execution{})

	r := gin.Default()
	api.RegisterRoutes(r)
	return r
}

func TestCreatePipeline(t *testing.T) {
	router := setupTestRouter()

	payload := map[string]interface{}{
		"name": "Pipeline Teste",
		"repo": "https://github.com/exemplo/teste",
		"steps": []map[string]interface{}{
			{"order": 1, "command": "echo Testando"},
		},
	}
	body, _ := json.Marshal(payload)

	req, _ := http.NewRequest("POST", "/pipelines", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusCreated, w.Code)
}

func TestGetAllPipelines(t *testing.T) {
	router := setupTestRouter()

	// Inserir dados manualmente
	db.DB.Create(&models.Pipeline{
		Name: "Teste",
		Repo: "https://github.com/x/y",
		Steps: []models.Step{
			{Order: 1, Command: "echo step"},
		},
	})

	req, _ := http.NewRequest("GET", "/pipelines", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Contains(t, w.Body.String(), "Teste")
}

func TestRunPipelineSuccess(t *testing.T) {
	router := setupTestRouter()

	pipeline := models.Pipeline{
		Name: "Runner Test",
		Repo: "https://fake.repo",
		Steps: []models.Step{
			{Order: 1, Command: "echo sucesso"},
		},
	}
	db.DB.Create(&pipeline)

	url := "/pipelines/run/" + strconv.Itoa(int(pipeline.ID))
	req, _ := http.NewRequest("POST", url, nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusOK, w.Code)
	assert.Contains(t, w.Body.String(), "sucesso")
}

func TestRunPipelineFailure(t *testing.T) {
	router := setupTestRouter()

	pipeline := models.Pipeline{
		Name: "Runner Falha",
		Repo: "https://fake.repo",
		Steps: []models.Step{
			{Order: 1, Command: "exit 1"},
		},
	}
	db.DB.Create(&pipeline)

	url := "/pipelines/run/" + strconv.Itoa(int(pipeline.ID))
	req, _ := http.NewRequest("POST", url, nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	assert.Equal(t, http.StatusInternalServerError, w.Code)
	assert.Contains(t, w.Body.String(), "Erro na execução")
}

E para rodar os testes exeutamos:

go test ./...