From ace27e1d81e9f4b171b90adad1e7e45818cf2b72 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Dec 2025 06:24:11 +0000 Subject: [PATCH 1/4] Initial plan From 0d9f7c848f4e08739e8542348fecbc3105dde386 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Dec 2025 06:30:08 +0000 Subject: [PATCH 2/4] Add Go server tests with coverage reporting to GitHub Actions workflow Co-authored-by: simihablo <218162243+simihablo@users.noreply.github.com> --- .github/workflows/static.yml | 75 +++++++++++++++++++++++++++++ .gitignore | 3 ++ Makefile | 5 ++ server-tests-output.txt | 20 ++++++++ server/go.mod | 3 ++ server/main.go | 47 ++++++++++++++++++ server/main_test.go | 93 ++++++++++++++++++++++++++++++++++++ 7 files changed, 246 insertions(+) create mode 100644 Makefile create mode 100644 server-tests-output.txt create mode 100644 server/go.mod create mode 100644 server/main.go create mode 100644 server/main_test.go diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 94b9f16..58fa623 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -12,6 +12,7 @@ jobs: runs-on: ubuntu-22.04 permissions: contents: write + pull-requests: write concurrency: group: ${{ github.workflow }}-${{ github.ref }} steps: @@ -24,6 +25,80 @@ jobs: with: node-version: '20' + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version: '1.21' + + - name: Run server tests with coverage + id: server_tests + run: | + echo "Running server tests..." + if make server-tests > server-tests-output.txt 2>&1; then + echo "status=success" >> $GITHUB_OUTPUT + echo "Tests passed ✅" >> $GITHUB_STEP_SUMMARY + else + echo "status=failure" >> $GITHUB_OUTPUT + echo "Tests failed ❌" >> $GITHUB_STEP_SUMMARY + exit 1 + fi + + # Extract coverage percentage + COVERAGE=$(grep "total:" server-tests-output.txt | awk '{print $3}') + echo "coverage=$COVERAGE" >> $GITHUB_OUTPUT + + # Display full output + cat server-tests-output.txt + + # Display coverage summary + echo "## Coverage Report" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + grep -A 100 "go tool cover" server-tests-output.txt | tail -n +2 >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + + - name: Upload coverage report + if: always() + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: | + server/coverage.out + server-tests-output.txt + retention-days: 30 + + - name: Comment on PR + if: github.event_name == 'pull_request' + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const output = fs.readFileSync('server-tests-output.txt', 'utf8'); + + // Extract test results + const coverageMatch = output.match(/total:.*?\((.*?)\)\s+(\d+\.\d+%)/); + const coverage = coverageMatch ? coverageMatch[2] : 'N/A'; + const status = '${{ steps.server_tests.outputs.status }}'; + const statusEmoji = status === 'success' ? '✅' : '❌'; + + // Get coverage details + const coverageDetails = output.split('go tool cover -func=coverage.out')[1] || ''; + + const statusText = status === 'success' ? 'PASSED' : 'FAILED'; + + const body = "## Server Tests Report " + statusEmoji + "\n\n" + + "**Status:** " + statusText + "\n" + + "**Coverage:** " + coverage + "\n\n" + + "### Coverage Details\n```\n" + coverageDetails.trim() + "\n```\n\n" + + "
\nFull Test Output\n\n```\n" + output + "\n```\n
"; + + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: body + }); + - name: Install dependencies and build run: | npm ci diff --git a/.gitignore b/.gitignore index a547bf3..716a94b 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ dist-ssr *.njsproj *.sln *.sw? + +# Go coverage files +*.out diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0e557c7 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +.PHONY: server-tests +server-tests: + @echo "Running server tests with coverage..." + cd server && go test -v -coverprofile=coverage.out -covermode=atomic ./... + cd server && go tool cover -func=coverage.out diff --git a/server-tests-output.txt b/server-tests-output.txt new file mode 100644 index 0000000..9a982be --- /dev/null +++ b/server-tests-output.txt @@ -0,0 +1,20 @@ +Running server tests with coverage... +cd server && go test -v -coverprofile=coverage.out -covermode=atomic ./... +=== RUN TestHealthHandler +--- PASS: TestHealthHandler (0.00s) +=== RUN TestGreetingHandler +=== RUN TestGreetingHandler/with_name_parameter +=== RUN TestGreetingHandler/without_name_parameter +=== RUN TestGreetingHandler/with_Spanish_name +--- PASS: TestGreetingHandler (0.00s) + --- PASS: TestGreetingHandler/with_name_parameter (0.00s) + --- PASS: TestGreetingHandler/without_name_parameter (0.00s) + --- PASS: TestGreetingHandler/with_Spanish_name (0.00s) +PASS +coverage: 64.3% of statements +ok github.com/simihablo/simihablo.github.io/server 0.003s coverage: 64.3% of statements +cd server && go tool cover -func=coverage.out +github.com/simihablo/simihablo.github.io/server/main.go:16: HealthHandler 100.0% +github.com/simihablo/simihablo.github.io/server/main.go:26: GreetingHandler 100.0% +github.com/simihablo/simihablo.github.io/server/main.go:39: main 0.0% +total: (statements) 64.3% diff --git a/server/go.mod b/server/go.mod new file mode 100644 index 0000000..76bcdda --- /dev/null +++ b/server/go.mod @@ -0,0 +1,3 @@ +module github.com/simihablo/simihablo.github.io/server + +go 1.24.11 diff --git a/server/main.go b/server/main.go new file mode 100644 index 0000000..f49c4b3 --- /dev/null +++ b/server/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" +) + +// HealthResponse represents the health check response +type HealthResponse struct { + Status string `json:"status"` + Message string `json:"message"` +} + +// HealthHandler handles health check requests +func HealthHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + response := HealthResponse{ + Status: "ok", + Message: "Server is running", + } + json.NewEncoder(w).Encode(response) +} + +// GreetingHandler handles greeting requests +func GreetingHandler(w http.ResponseWriter, r *http.Request) { + name := r.URL.Query().Get("name") + if name == "" { + name = "World" + } + + w.Header().Set("Content-Type", "application/json") + response := map[string]string{ + "greeting": fmt.Sprintf("Hola, %s!", name), + } + json.NewEncoder(w).Encode(response) +} + +func main() { + http.HandleFunc("/health", HealthHandler) + http.HandleFunc("/greet", GreetingHandler) + + fmt.Println("Server starting on :8080") + if err := http.ListenAndServe(":8080", nil); err != nil { + fmt.Printf("Server failed to start: %v\n", err) + } +} diff --git a/server/main_test.go b/server/main_test.go new file mode 100644 index 0000000..3f49e46 --- /dev/null +++ b/server/main_test.go @@ -0,0 +1,93 @@ +package main + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" +) + +func TestHealthHandler(t *testing.T) { + req, err := http.NewRequest("GET", "/health", nil) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(HealthHandler) + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + var response HealthResponse + if err := json.NewDecoder(rr.Body).Decode(&response); err != nil { + t.Fatalf("Failed to decode response: %v", err) + } + + if response.Status != "ok" { + t.Errorf("handler returned unexpected status: got %v want %v", + response.Status, "ok") + } + + if response.Message != "Server is running" { + t.Errorf("handler returned unexpected message: got %v want %v", + response.Message, "Server is running") + } +} + +func TestGreetingHandler(t *testing.T) { + tests := []struct { + name string + queryParam string + expectedGreet string + }{ + { + name: "with name parameter", + queryParam: "?name=Juan", + expectedGreet: "Hola, Juan!", + }, + { + name: "without name parameter", + queryParam: "", + expectedGreet: "Hola, World!", + }, + { + name: "with Spanish name", + queryParam: "?name=María", + expectedGreet: "Hola, María!", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req, err := http.NewRequest("GET", "/greet"+tt.queryParam, nil) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(GreetingHandler) + handler.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("handler returned wrong status code: got %v want %v", + status, http.StatusOK) + } + + var response map[string]string + if err := json.NewDecoder(rr.Body).Decode(&response); err != nil { + t.Fatalf("Failed to decode response: %v", err) + } + + if greeting, ok := response["greeting"]; !ok { + t.Error("response missing 'greeting' field") + } else if greeting != tt.expectedGreet { + t.Errorf("handler returned unexpected greeting: got %v want %v", + greeting, tt.expectedGreet) + } + }) + } +} From 0afd807065dd0c6b1647fbae58beffdf60b486f5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Dec 2025 06:30:36 +0000 Subject: [PATCH 3/4] Remove test output file and update gitignore Co-authored-by: simihablo <218162243+simihablo@users.noreply.github.com> --- .gitignore | 3 +++ server-tests-output.txt | 20 -------------------- 2 files changed, 3 insertions(+), 20 deletions(-) delete mode 100644 server-tests-output.txt diff --git a/.gitignore b/.gitignore index 716a94b..aa872c7 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ dist-ssr # Go coverage files *.out + +# Test output files +server-tests-output.txt diff --git a/server-tests-output.txt b/server-tests-output.txt deleted file mode 100644 index 9a982be..0000000 --- a/server-tests-output.txt +++ /dev/null @@ -1,20 +0,0 @@ -Running server tests with coverage... -cd server && go test -v -coverprofile=coverage.out -covermode=atomic ./... -=== RUN TestHealthHandler ---- PASS: TestHealthHandler (0.00s) -=== RUN TestGreetingHandler -=== RUN TestGreetingHandler/with_name_parameter -=== RUN TestGreetingHandler/without_name_parameter -=== RUN TestGreetingHandler/with_Spanish_name ---- PASS: TestGreetingHandler (0.00s) - --- PASS: TestGreetingHandler/with_name_parameter (0.00s) - --- PASS: TestGreetingHandler/without_name_parameter (0.00s) - --- PASS: TestGreetingHandler/with_Spanish_name (0.00s) -PASS -coverage: 64.3% of statements -ok github.com/simihablo/simihablo.github.io/server 0.003s coverage: 64.3% of statements -cd server && go tool cover -func=coverage.out -github.com/simihablo/simihablo.github.io/server/main.go:16: HealthHandler 100.0% -github.com/simihablo/simihablo.github.io/server/main.go:26: GreetingHandler 100.0% -github.com/simihablo/simihablo.github.io/server/main.go:39: main 0.0% -total: (statements) 64.3% From 1de73ea1d2fd9f9432dea6507307bb1b291dbac2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Dec 2025 06:32:36 +0000 Subject: [PATCH 4/4] Fix Go version mismatch in workflow to match go.mod Co-authored-by: simihablo <218162243+simihablo@users.noreply.github.com> --- .github/workflows/static.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 58fa623..fb1be15 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -28,7 +28,7 @@ jobs: - name: Setup Go uses: actions/setup-go@v5 with: - go-version: '1.21' + go-version: '1.24' - name: Run server tests with coverage id: server_tests