Skip to content

feat(groq): A tiny Dockerized HTTP server that serves deterministic post‑apocalyptic survival tips.#4259

Open
polsala wants to merge 1 commit intomainfrom
ai/groq-20260419-0601
Open

feat(groq): A tiny Dockerized HTTP server that serves deterministic post‑apocalyptic survival tips.#4259
polsala wants to merge 1 commit intomainfrom
ai/groq-20260419-0601

Conversation

@polsala
Copy link
Copy Markdown
Owner

@polsala polsala commented Apr 19, 2026

Implementation Summary

  • Utility: nightly-apocalypse-tip-server
  • Provider: groq
  • Location: docker-tools/nightly-nightly-apocalypse-tip-serve
  • Files Created: 4
  • Description: A tiny Dockerized HTTP server that serves deterministic post‑apocalyptic survival tips.

Rationale

  • Automated proposal from the Groq generator delivering a fresh community utility.
  • This utility was generated using the groq AI provider.

Why safe to merge

  • Utility is isolated to docker-tools/nightly-nightly-apocalypse-tip-serve.
  • README + tests ship together (see folder contents).
  • No secrets or credentials touched.
  • All changes are additive and self-contained.

Test Plan

  • Follow the instructions in the generated README at docker-tools/nightly-nightly-apocalypse-tip-serve/README.md
  • Run tests located in docker-tools/nightly-nightly-apocalypse-tip-serve/tests/

Links

  • Generated docs and examples committed alongside this change.

Mock Justification

  • Not applicable; generator did not introduce new mocks.

@polsala
Copy link
Copy Markdown
Owner Author

polsala commented Apr 19, 2026

🤖 Review by GROQ Agent

✅ What's solid

  • Self‑contained utility – All source, Dockerfile, README and tests live under a dedicated folder, keeping the rest of the repo untouched.
  • Deterministic behavior – Using the SEED env‑var to pick a tip makes the service easy to test and reason about.
  • Multi‑stage Docker build – The builder image (golang:1.22-alpine) is discarded, resulting in a tiny final image (alpine:3.19).
  • Simple, readable Go code – The handler is short, uses only the standard library, and logs startup information.
  • Shell‑based integration test – Spins up the container, hits the endpoint, and cleans up afterwards, which gives confidence that the Docker image works end‑to‑end.

🧪 Tests

Observation Recommendation
Only one happy‑path test (SEED=7) is present. Add a small unit‑test suite that exercises the handler directly with net/http/httptest. This avoids the overhead of building/running a container for every CI run. Example:

go\nfunc TestHandler_DefaultSeed(t *testing.T) {\n req := httptest.NewRequest(http.MethodGet, \"/\", nil)\n w := httptest.NewRecorder()\n os.Unsetenv(\"SEED\")\n handler(w, req)\n got := strings.TrimSpace(w.Body.String())\n if got != tips[0] {\n t.Fatalf(\"expected %q, got %q\", tips[0], got)\n }\n}\n
No test for malformed or negative seeds. Add a couple of table‑driven tests covering:
• non‑numeric seed (e.g. SEED=abc)
• negative seed (e.g. SEED=-3)
• seed larger than len(tips)
These can verify that the fallback index is 0 and that the server never panics.
The integration test uses a fixed sleep 1 to wait for the server. Replace the static sleep with a retry loop that probes the endpoint until it returns a 200 or a timeout is reached. This makes the test more robust on slower CI runners.
No test for graceful shutdown or signal handling. Consider adding a test that sends SIGTERM to the container and asserts that the process exits cleanly (exit code 0). This will also surface the need for proper signal handling in main.

🔒 Security

  • Running as root – The final image inherits the default root user from Alpine. Running a network‑exposed service as root is unnecessary and risky.
    Fix: Add a non‑privileged user in the Dockerfile:

    FROM alpine:3.19
    RUN addgroup -S tip && adduser -S -G tip tip
    COPY --from=builder /app/tip-server /tip-server
    USER tip
    EXPOSE 8080
    ENTRYPOINT ["/tip-server"]
  • No request timeouts – The server uses http.ListenAndServe with the default net/http server, which has no read/write timeouts. A malicious client could hold connections open indefinitely.
    Fix: Configure a http.Server with sensible limits:

    srv := &http.Server{
        Addr:         ":8080",
        Handler:      http.DefaultServeMux,
        ReadTimeout:  5 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  120 * time.Second,
    }
    log.Println("Starting server on :8080")
    log.Fatal(srv.ListenAndServe())
  • Input validationstrconv.Atoi silently treats a leading “+” or “-” as valid. While the modulo operation works, a negative seed yields a negative index before the modulo, which can panic if not handled.
    Fix: Normalise the seed to a non‑negative value:

    if s, err := strconv.Atoi(seedStr); err == nil {
        if s < 0 {
            s = -s
        }
        idx = s % len(tips)
    }
  • Docker image hardening – Consider adding a HEALTHCHECK instruction so orchestration tools can detect a stuck container:

    HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost:8080/ || exit 1

🧩 Docs/DX

  • README clarity – The current README covers build/run/testing, but could be expanded:

    • Explain the deterministic nature of the tip selection (e.g., “SEED modulo number‑of‑tips`).
    • Document the default behavior when SEED is unset or invalid.
    • Provide a quick command to stop the container (docker stop tip_server).
    • Mention the health‑check (if added) and how to inspect logs (docker logs tip_server).
  • Example usage – Show a couple of seed values and the resulting tip, e.g.:

    $ SEED=0 docker run --rm -e SEED=0 tip-test
    Always keep a spare can opener.
    $ SEED=13 docker run --rm -e SEED=13 tip-test
    Scavenge with a purpose.
    
  • Contribution guidelines – If future contributors will add more tips, note where to edit the tips slice and remind them to keep the slice length in sync with any tests that rely on it.

  • License – Ensure the new folder includes a LICENSE header or inherits the repo’s license (add a comment at the top of main.go if required).

🧱 Mocks/Fakes

  • No external dependencies – The service does not call out to databases, APIs, or other services, so mocks are unnecessary for the core functionality.

  • Testing strategy suggestion – For more granular unit tests, replace the real os.Getenv call with a small wrapper that can be stubbed. Example:

    var getEnv = os.Getenv // allow override in tests
    
    func handler(w http.ResponseWriter, r *http.Request) {
        seedStr := getEnv("SEED")
        // …
    }

    In tests you can then set getEnv = func(key string) string { return "42" } without touching the process environment. This keeps the unit tests pure and faster.


Overall, the implementation is clean and functional. Adding the security hardening steps, expanding the test matrix, and polishing the documentation will make the utility production‑ready and easier for future contributors to maintain.

@polsala
Copy link
Copy Markdown
Owner Author

polsala commented Apr 19, 2026

🤖 Review by GEMINI Agent

✅ What's solid

  • The use of Docker provides a self-contained and easily deployable utility. The multi-stage build in the Dockerfile is a good practice for minimizing image size and attack surface.
  • The SEED environment variable is an effective mechanism for achieving deterministic tip selection, which is excellent for testing and reproducibility.
  • The test_server.sh script includes robust cleanup using a trap command, ensuring no lingering Docker resources after test execution.
  • The Go application is concise and focused, demonstrating a clear understanding of its purpose.

🧪 Tests

  • The test_server.sh script provides a good end-to-end integration test, covering the build, run, and query cycle of the Dockerized server.
  • The current test only validates one specific SEED value (7). To ensure the modulo arithmetic for tip selection works correctly across the range of possible indices, consider adding tests for:
    • SEED=0 (first tip)
    • SEED=len(tips)-1 (last tip)
    • SEED=len(tips) (should wrap around to the first tip)
    • A negative SEED value (though strconv.Atoi would handle this, the modulo might behave differently in Go for negative numbers, s % len(tips) could result in a negative index if s is negative).
  • The sleep 1 in test_server.sh can introduce flakiness. A more robust approach would be to implement a health check or a retry loop that waits for the server to become responsive before querying it.
    # Wait for the server to be ready
    for i in $(seq 1 10); do
        if curl -s http://localhost:18080/ > /dev/null; then
            echo "Server is up."
            break
        fi
        echo "Waiting for server to start..."
        sleep 0.5
        if [ $i -eq 10 ]; then
            echo "Server did not start in time."
            exit 1
        fi
    done
  • While the integration test is valuable, adding unit tests for the handler function in main.go would allow for faster feedback on logic changes and better isolation of the tip selection mechanism.

🔒 Security

  • The application's simplicity and lack of external dependencies (beyond standard Go libraries) inherently limit its attack surface. No secrets or credentials are handled.
  • The strconv.Atoi conversion for the SEED environment variable does not explicitly handle parsing errors beyond defaulting idx to 0. While not a direct security vulnerability, robust error handling is good practice. Consider logging the error for invalid SEED values.
        if seedStr != "" {
            s, err := strconv.Atoi(seedStr)
            if err != nil {
                log.Printf("Warning: Invalid SEED value '%s' provided, defaulting to 0. Error: %v", seedStr, err)
                idx = 0 // Default to 0 if conversion fails
            } else {
                idx = s % len(tips)
            }
        }
  • The log.Fatal(err) call in main will cause the container to exit if http.ListenAndServe fails. For a simple utility, this is acceptable, but in more complex scenarios, a graceful shutdown or retry mechanism might be considered.

🧩 Docs/DX

  • The README.md is clear and provides all necessary instructions for building, running, and testing the server.
  • The directory name nightly-nightly-apocalypse-tip-serve is somewhat redundant. Consider shortening it to nightly-apocalypse-tip-server for improved readability and conciseness.
  • To enhance the developer experience, the README.md could include an example of the server's output when run, giving users an immediate understanding of its functionality.
    ## Example Output
    
    Water is more valuable than gold.
  • For the docker run command in the README, it might be helpful to include docker stop tip_server and docker rm tip_server commands for manual cleanup, mirroring the robustness of the test script.

🧱 Mocks/Fakes

  • As noted in the PR body, no explicit mocks were introduced. However, the SEED environment variable effectively serves as a mechanism to "mock" or control the deterministic behavior of the tip selection, which is a good pattern for making the component testable without external dependencies.
  • For future enhancements, if the list of tips were to become dynamic (e.g., loaded from a file or an external service), introducing an interface for a TipProvider would allow for easier mocking and unit testing of the handler logic in isolation. For the current scope, this is not a critical requirement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant