diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a2a3452 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,35 @@ +name: CI + +on: + push: + branches: [develop] + pull_request: + branches: [develop] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v5 + with: + go-version: "1.25" + + - name: Check formatting + run: | + fmt_out="$(gofmt -l .)" + if [ -n "$fmt_out" ]; then + echo "The following files are not gofmt'd:" + echo "$fmt_out" + exit 1 + fi + + - name: Vet + run: go vet ./... + + - name: Test + run: go test ./... -race -cover + + - name: Build + run: go build ./... diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..2e6b236 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,31 @@ +name: Build and Publish Docker Image + +on: + push: + branches: [develop] + +permissions: + contents: read + packages: write + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: | + ghcr.io/${{ github.repository }}:latest + ghcr.io/${{ github.repository }}:${{ github.sha }} diff --git a/internal/handlers/health_test.go b/internal/handlers/health_test.go new file mode 100644 index 0000000..419016c --- /dev/null +++ b/internal/handlers/health_test.go @@ -0,0 +1,23 @@ +package handlers + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func TestHealthHandler(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/health", nil) + rec := httptest.NewRecorder() + + HealthHandler(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code) + } + + want := `{"status":"ok"}` + "\n" + if got := rec.Body.String(); got != want { + t.Errorf("body = %q, want %q", got, want) + } +} diff --git a/internal/handlers/hello_test.go b/internal/handlers/hello_test.go new file mode 100644 index 0000000..11a0931 --- /dev/null +++ b/internal/handlers/hello_test.go @@ -0,0 +1,41 @@ +package handlers + +import ( + "net/http" + "net/http/httptest" + "testing" + + "go-demo/internal/service" +) + +func TestHelloHandler(t *testing.T) { + h := NewHelloHandler(service.NewHelloService()) + + req := httptest.NewRequest(http.MethodGet, "/hello?name=Test", nil) + rec := httptest.NewRecorder() + + h.ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code) + } + + want := `{"message":"Hello, Test!"}` + "\n" + if got := rec.Body.String(); got != want { + t.Errorf("body = %q, want %q", got, want) + } +} + +func TestHelloHandlerDefaultName(t *testing.T) { + h := NewHelloHandler(service.NewHelloService()) + + req := httptest.NewRequest(http.MethodGet, "/hello", nil) + rec := httptest.NewRecorder() + + h.ServeHTTP(rec, req) + + want := `{"message":"Hello, World!"}` + "\n" + if got := rec.Body.String(); got != want { + t.Errorf("body = %q, want %q", got, want) + } +} diff --git a/internal/service/hello_test.go b/internal/service/hello_test.go new file mode 100644 index 0000000..f285bae --- /dev/null +++ b/internal/service/hello_test.go @@ -0,0 +1,20 @@ +package service + +import "testing" + +func TestGreet(t *testing.T) { + tests := []struct { + name string + want string + }{ + {name: "", want: "Hello, World!"}, + {name: "Go", want: "Hello, Go!"}, + } + + s := NewHelloService() + for _, tt := range tests { + if got := s.Greet(tt.name); got != tt.want { + t.Errorf("Greet(%q) = %q, want %q", tt.name, got, tt.want) + } + } +}