Skip to content

Break out GraphiQL tests #51

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 9, 2025
Merged

Break out GraphiQL tests #51

merged 4 commits into from
Jul 9, 2025

Conversation

ejfine
Copy link
Contributor

@ejfine ejfine commented Jul 9, 2025

Why is this change necessary?

Add some more graphiql tests and break them into separate file for easier management

How does this change address the issue?

Does it

What side effects does this change have?

None

How is this change tested?

Downstream repo

Summary by CodeRabbit

  • New Features

    • Improved offline support for the GraphiQL interface, ensuring it no longer relies on external CDN resources.
    • Enhanced error handling for missing CDN URLs in the GraphiQL HTML template.
  • Bug Fixes

    • The GraphiQL endpoint now properly serves content without referencing external URLs.
  • Tests

    • Added new unit tests to verify offline GraphiQL functionality and error handling.
    • Removed outdated test for the GraphQL route rendering.
  • Documentation

    • Updated Docker usage example to reflect the correct exposed port (3000 instead of 80).

@ejfine ejfine requested a review from Copilot July 9, 2025 18:51
@ejfine ejfine self-assigned this Jul 9, 2025
Copy link

coderabbitai bot commented Jul 9, 2025

Walkthrough

The changes update the frontend Dockerfile template's example port mapping, refactor the backend's GraphiQL HTML offline generation with improved error handling, and introduce a dedicated exception for missing CDN URLs. Backend tests are revised: a healthcheck test is removed, and new unit tests for GraphiQL offline rendering and error handling are added.

Changes

File(s) Change Summary
template/frontend/Dockerfile.jinja Updated example Docker run command to map port 3000 instead of 80.
template/backend/src/backend_api/app_def.py.jinja Refactored GraphiQL offline HTML logic; added error class, helper functions, and improved error handling.
template/backend/tests/unit/test_healthcheck.py Removed test for GraphQL route rendering; minor formatting changes.
template/backend/tests/unit/test_graphiql.py Added new tests for GraphiQL offline HTML generation and error handling.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant FastAPI_App
    participant OfflineGraphQLRouter
    participant app_def.generate_offline_graphiql_html

    Client->>FastAPI_App: GET /api/graphql
    FastAPI_App->>OfflineGraphQLRouter: Handle request
    OfflineGraphQLRouter->>app_def.generate_offline_graphiql_html: Generate offline GraphiQL HTML
    app_def.generate_offline_graphiql_html->>app_def._get_graphiql_html_base: Read base HTML
    app_def.generate_offline_graphiql_html->>app_def.CdnUrlNotFoundInHtmlError: (If CDN URL missing) Raise error
    app_def.generate_offline_graphiql_html-->>OfflineGraphQLRouter: Return offline HTML
    OfflineGraphQLRouter-->>FastAPI_App: Render response
    FastAPI_App-->>Client: Return HTML
Loading

Poem

In the warren of code where the GraphiQL bunnies dwell,
We swapped out port eighty—three thousand rings the bell!
With new checks for CDNs, and errors caught with care,
Our tests now hop with confidence, no bugs left to snare.
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR extracts GraphiQL tests into their own file, enhances offline GraphiQL HTML generation, and cleans up related test and configuration files.

  • Adds dedicated test_graphiql.py with tests for missing CDN URLs and rendering of the GraphQL route.
  • Refactors generate_offline_graphiql_html in app_def.py.jinja, introducing a helper, custom exception, and middleware adjustments.
  • Cleans up test_healthcheck.py by removing duplicate GraphQL tests and fixes comment in the frontend Dockerfile.

Reviewed Changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
template/backend/tests/unit/test_graphiql.py New GraphiQL-specific unit tests
template/backend/tests/unit/test_healthcheck.py Removed raw tags, deduplicated GraphQL test
template/backend/src/backend_api/app_def.py.jinja Extracted HTML helper, added exception and loop logic
template/frontend/Dockerfile.jinja Updated port mapping comment for Nuxt container

@ejfine ejfine merged commit 64c886a into main Jul 9, 2025
11 of 12 checks passed
@ejfine ejfine deleted the more-tests branch July 9, 2025 18:56
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
template/{% if has_backend %}backend{% endif %}/src/backend_api/app_def.py.jinja (1)

102-126: Proper error handling and middleware setup.

Good practices observed:

  • Wrapping initialization in try-except for better error visibility
  • Correct ordering with static files mounted after route definitions
  • Clean override of the GraphQL IDE rendering

Note: The permissive CORS configuration (allow_origins=["*"]) is appropriate for the stated intranet use case but should be reconsidered if this template is used for internet-facing applications.

Consider adding a more descriptive comment about the CORS configuration:

-        allow_origins=["*"],  # Super permissive CORS setting since this is for intranet
+        allow_origins=["*"],  # WARNING: Permissive CORS for intranet use only. Restrict origins for internet-facing deployments.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a42f8ed and 4746a26.

📒 Files selected for processing (4)
  • template/frontend/{% if not deploy_as_executable %}Dockerfile{% endif %}.jinja (1 hunks)
  • template/{% if has_backend %}backend{% endif %}/src/backend_api/app_def.py.jinja (2 hunks)
  • template/{% if has_backend %}backend{% endif %}/tests/unit/test_healthcheck.py (3 hunks)
  • template/{% if has_backend %}backend{% endif %}/tests/unit/{% if backend_uses_graphql %}test_graphiql.py{% endif %} (1 hunks)
🔇 Additional comments (12)
template/{% if has_backend %}backend{% endif %}/tests/unit/test_healthcheck.py (4)

1-1: LGTM: Import is correctly used.

The time import is needed for the time.sleep(0.001) call in the shutdown test.


30-30: LGTM: Formatting adjustment.

The assertion logic remains correct and the formatting is clean.


45-45: LGTM: Formatting adjustment.

The assertion logic remains correct and the formatting is clean.


1-46: Approve refactoring: GraphQL test appropriately moved.

The removal of the GraphQL test function aligns well with the PR objective to break out GraphiQL tests into a separate dedicated file. This improves test organization and maintainability by keeping healthcheck tests focused on their core responsibility.

template/{% if has_backend %}backend{% endif %}/tests/unit/{% if backend_uses_graphql %}test_graphiql.py{% endif %} (3)

1-8: LGTM! Well-organized import structure.

The imports are clean, relevant, and follow standard Python conventions. The inclusion of the new CdnUrlNotFoundInHtmlError exception aligns with the refactored error handling functionality.


10-18: LGTM! Well-designed error handling test.

The test properly validates the new CdnUrlNotFoundInHtmlError exception handling. The mocking approach, while accessing a private method, is appropriately documented and necessary for testing the error condition.

Note: The test may be fragile if the internal implementation of _get_graphiql_html_base changes, but this appears to be an acceptable trade-off for comprehensive error testing.


21-28: LGTM! Comprehensive integration test for GraphQL route.

The test effectively validates the offline GraphiQL rendering by checking:

  • Successful response (200 OK)
  • Presence of GraphiQL content
  • Absence of external CDN URLs

This provides good coverage for the refactored GraphiQL functionality and properly validates the offline rendering behavior.

template/{% if has_backend %}backend{% endif %}/src/backend_api/app_def.py.jinja (4)

66-66: Clean implementation of the shutdown response.

The return statement correctly instantiates the response model without unnecessary parameters, relying on the default values defined in the model.


69-72: Well-structured custom exception class.

The CdnUrlNotFoundInHtmlError follows Python best practices with clear error messaging that includes the problematic URL for easier debugging.


74-77: Good separation of concerns for testability.

Extracting the file reading logic into a separate function enables easy mocking in unit tests, which aligns with the PR's objective of improving test management.


79-99: Improve maintainability of offline GraphiQL HTML generation

– Extract CDN URLs dynamically instead of hard-coding versions to avoid breakage when Strawberry bumps GraphiQL.
– Removing SRI for local files is low-risk (SRI’s main threat model is CDN tampering), but if you want defense-in-depth you could compute and inject fresh hashes for your bundled assets.
– Ensure your generated project’s /static/graphiql/ directory contains all required files (see README “Obtaining the GraphiQL files to bundle” for curl commands).

Suggested diff:

 def generate_offline_graphiql_html() -> str:
     offline_graphiql_html = _get_graphiql_html_base()
-    cdn_urls = [
-        "https://unpkg.com/[email protected]/umd/react.production.min.js",
-        # …rest of hard-coded URLs
-    ]
+    import re
+    # pull in whatever JS/CSS URLs strawberry’s HTML is using today
+    cdn_pattern = r'https://unpkg\.com/[^"]+\.(?:js|css)'
+    cdn_urls = re.findall(cdn_pattern, offline_graphiql_html)
+    if not cdn_urls:
+        raise ValueError("No GraphiQL CDN URLs found in HTML; offline generation cannot proceed")
 
     for cdn_url in cdn_urls:
         if cdn_url not in offline_graphiql_html:
             raise CdnUrlNotFoundInHtmlError(cdn_url)
         filename = cdn_url.split("/")[-1]
         local_path = f"/static/graphiql/{filename}"
         offline_graphiql_html = offline_graphiql_html.replace(cdn_url, local_path)
 
     return "\n".join(
         # remove integrity attributes (they refer to CDN hashes)
         [line for line in offline_graphiql_html.splitlines() if not line.strip().startswith("integrity=")]
     )

fix_required

template/frontend/{% if not deploy_as_executable %}Dockerfile{% endif %}.jinja (1)

36-38: Review Comment Update: EXPOSE Port and Nginx Listen Directive Are Consistent

The EXPOSE {{ frontend_deployed_port_number }} in the Dockerfile template matches the listen {{ frontend_deployed_port_number }} in the Nginx server block template—there is no mismatch:

• template/frontend/{% if not deploy_as_executable %}Dockerfile{% endif %}.jinja (line 37):
EXPOSE {{ frontend_deployed_port_number }}

• template/frontend/{% if not deploy_as_executable %}default.conf.template{% endif %}.jinja (line 2):
listen {{ frontend_deployed_port_number }};

No changes needed.

Likely an incorrect or invalid review comment.

Comment on lines +5 to 6
# docker run -p {% endraw %}{{ frontend_deployed_port_number }}{% raw %}:3000 my-nuxt-app

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Port mapping in the example command is likely wrong / misleading

docker run -p {{ frontend_deployed_port_number }}:3000 … assumes the container is listening on port 3000.
However, the production stage runs Nginx (which normally listens on 80) and the default.conf.template is rendered without any $FRONTEND_PORT substitution. Unless that template was also updated to listen 3000;, the container will still listen on 80, making the mapping (host:{{frontend_deployed_port_number}} → container:3000) invalid.

Recommended fix (minimum):

-# docker run -p {% endraw %}{{ frontend_deployed_port_number }}{% raw %}:3000 my-nuxt-app
+# docker run -p {% endraw %}{{ frontend_deployed_port_number }}{% raw %}:80 my-nuxt-app

or (better) update default.conf.template to listen on {{ frontend_deployed_port_number }} and keep the current example.

Please verify which side you really want to expose and keep the example + EXPOSE directive consistent with the Nginx config.


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# Locate and inspect the Nginx default configuration template
nginx_template=$(fd default.conf.template template/frontend || true)
if [[ -n "$nginx_template" ]]; then
  echo "== Nginx template found at: $nginx_template =="
  grep -R -n "listen" "$nginx_template" || echo "No 'listen' directives in $nginx_template"
else
  echo "No default.conf.template found under template/frontend"
fi

# Locate and inspect the Dockerfile Jinja template for EXPOSE directives
dockerfile_template=$(fd Dockerfile.jinja template/frontend || true)
if [[ -n "$dockerfile_template" ]]; then
  echo "== Dockerfile template found at: $dockerfile_template =="
  grep -R -n "EXPOSE" "$dockerfile_template" || echo "No 'EXPOSE' directives in $dockerfile_template"
else
  echo "No Dockerfile.jinja found under template/frontend"
fi

Length of output: 896


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

# List all Jinja template files under template/frontend
echo "=== .jinja files in template/frontend ==="
fd -e jinja template/frontend

# Search for EXPOSE directives in those Jinja files
echo "=== Searching for EXPOSE in Jinja templates ==="
grep -R -n "EXPOSE" template/frontend/*.jinja || echo "No 'EXPOSE' directives found"

Length of output: 518


Update Docker run example to match Nginx listen port

The Nginx template (template/frontend/{% if not deploy_as_executable %}default.conf.template{% endif %}.jinja, line 2) sets:

listen {{ frontend_deployed_port_number }};

so the container listens on the same {{ frontend_deployed_port_number }}, not port 3000. The example here therefore maps the wrong container port.

• File: template/frontend/{% if not deploy_as_executable %}Dockerfile{% endif %}.jinja
Lines 5–6 (example run command)
• File: template/frontend/{% if not deploy_as_executable %}default.conf.template{% endif %}.jinja
Line 2 (listen {{ frontend_deployed_port_number }};)

Recommended changes:

-# docker run -p {{ frontend_deployed_port_number }}:3000 my-nuxt-app
+# docker run -p {{ frontend_deployed_port_number }}:{{ frontend_deployed_port_number }} my-nuxt-app

Additionally, ensure your Dockerfile template exposes that port:

EXPOSE {{ frontend_deployed_port_number }}

This keeps the EXPOSE, Nginx listen, and docker run example all consistent.

🤖 Prompt for AI Agents
In template/frontend/{% if not deploy_as_executable %}Dockerfile{% endif
%}.jinja around lines 5 to 6, the docker run example incorrectly maps the
container port 3000 instead of {{ frontend_deployed_port_number }} which is the
port Nginx listens on as set in template/frontend/{% if not deploy_as_executable
%}default.conf.template{% endif %}.jinja line 2. Update the docker run command
to map {{ frontend_deployed_port_number }} to {{ frontend_deployed_port_number
}} (e.g., -p {{ frontend_deployed_port_number }}:{{
frontend_deployed_port_number }}). Also, ensure the Dockerfile template includes
an EXPOSE directive for {{ frontend_deployed_port_number }} to keep port
configuration consistent.

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