Skip to content

djeusette/gql_case

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GqlCase

GqlCase is a comprehensive GraphQL testing library designed specifically for Absinthe projects. It provides powerful macros and utilities to easily test GraphQL queries and mutations with support for authentication, custom headers, and import resolution.

Key Features

  • Easy GraphQL testing - Load GraphQL documents from files or strings with simple macros
  • Authentication support - Seamless JWT bearer token integration for authenticated queries
  • Flexible header management - Configure default headers with override capabilities
  • Import resolution - Support for #import statements in GraphQL files
  • Security by design - Built-in validation and size limits for safe operation

Installation

Add gql_case to your list of dependencies in mix.exs:

def deps do
  [
    {:gql_case, "~> 0.5"}
  ]
end

Basic Setup

To use GqlCase in your tests, first create a configuration module:

defmodule MyApp.GqlCase do
  use GqlCase,
    gql_path: "/graphql",
    jwt_bearer_fn: &MyApp.Guardian.encode_and_sign/1,
    default_headers: [
      {"x-app-version", "1.0.0"},
      {"x-client-type", "test"}
    ]
end

Then use it in your test modules:

defmodule MyApp.UserQueryTest do
  use ExUnit.Case
  use MyApp.GqlCase

  @endpoint MyApp.Endpoint

  load_gql_file("queries/GetUser.gql")

  test "fetches user data" do
    result = query_gql(variables: %{id: "123"})
    assert %{"data" => %{"user" => %{"id" => "123"}}} = result
  end
end

Core Configuration

The use GqlCase directive accepts several required and optional parameters:

Required Parameters

  • gql_path - The endpoint path for GraphQL requests (e.g., "/graphql")
  • jwt_bearer_fn - Function to generate JWT tokens for authentication (arity 1)

Optional Parameters

  • default_headers - List of default headers applied to all requests
defmodule MyApp.GqlCase do
  use GqlCase,
    gql_path: "/api/graphql",
    jwt_bearer_fn: &MyApp.Auth.create_token/1,
    default_headers: [
      {"accept", "application/json"},
      {"x-api-version", "v1"}
    ]
end

Loading GraphQL Documents

GqlCase provides two ways to load GraphQL documents into your test modules:

Loading from Files

Use load_gql_file/1 to load GraphQL documents from external files:

defmodule MyApp.ProductTest do
  use MyApp.GqlCase
  
  # Load from relative path
  load_gql_file("queries/GetProducts.gql")
  
  test "gets products" do
    result = query_gql()
    assert %{"data" => %{"products" => products}} = result
  end
end

Loading from Strings

Use load_gql_string/1 for inline GraphQL queries:

defmodule MyApp.SimpleTest do
  use MyApp.GqlCase
  
  load_gql_string """
  query GetHello {
    hello
  }
  """
  
  test "says hello" do
    result = query_gql()
    assert %{"data" => %{"hello" => "Hello, World!"}} = result
  end
end

Import System

GqlCase supports GraphQL import statements for modular query organization:

# fragments/UserFields.gql
fragment UserFields on User {
  id
  name
  email
}

# queries/GetUser.gql  
#import "fragments/UserFields.gql"

query GetUser($id: ID!) {
  user(id: $id) {
    ...UserFields
  }
}

Basic Query Execution

Execute loaded GraphQL documents using the query_gql/1 macro:

Simple Queries

result = query_gql()
assert %{"data" => %{"hello" => "Hello, World!"}} = result

Queries with Variables

result = query_gql(variables: %{id: "123", name: "John"})
assert %{"data" => %{"user" => %{"id" => "123"}}} = result

Authentication Features

GqlCase seamlessly integrates with JWT-based authentication systems:

Authenticated Queries

Pass a current_user to automatically generate and include JWT tokens:

user = %{id: "123", email: "user@example.com"}
result = query_gql(current_user: user)

JWT Bearer Function

The JWT bearer function should accept a user struct or map and return {:ok, token, claims}:

defmodule MyApp.Auth do
  def create_token(user) do
    Guardian.encode_and_sign(user, %{}, ttl: {1, :hour})
  end
end

Header Management

GqlCase provides a flexible header management system with multiple levels of configuration:

Default Headers

Set default headers that apply to all requests in your configuration:

use GqlCase,
  gql_path: "/graphql", 
  jwt_bearer_fn: &MyApp.Auth.create_token/1,
  default_headers: [
    {"x-app-version", "1.0.0"},
    {"accept-language", "en-US"}
  ]

Module-Specific Headers

Override or add headers for specific test modules:

defmodule MyApp.AdminTest do
  use ExUnit.Case
  use MyApp.GqlCase, headers: [
    {"x-admin-role", "super"},
    {"x-feature-flag", "admin-panel"}
  ]
  
  load_gql_string "query { adminData }"
  
  test "accesses admin data" do
    result = query_gql()
    # Request includes both default headers and module-specific headers
  end
end

Runtime Header Overrides

Add or override headers for individual queries:

result = query_gql(
  variables: %{id: "123"},
  headers: [
    {"x-request-id", "abc-123"},
    {"x-app-version", "2.0.0"}  # Overrides default
  ]
)

Header Priority System

Headers are merged with the following priority (highest to lowest):

  1. Runtime headers (passed to query_gql/1)
  2. Authorization header (generated from current_user)
  3. Module-specific headers (from use MyApp.GqlCase, headers: [...])
  4. Default headers (from configuration)
  5. Built-in headers ({"content-type", "application/json"})

Advanced Usage

Error Handling Patterns

GqlCase validates GraphQL documents and provides detailed error information:

# File not found
load_gql_file("nonexistent.gql")  # Raises LoaderError

# Invalid GraphQL syntax  
load_gql_string "query { invalid syntax }"  # Raises ParseError

# Missing import
load_gql_file("query_with_missing_import.gql")  # Raises ImportError

Import Resolution

Imports are resolved relative to the importing file's directory:

project/
├── test/
│   └── queries/
│       ├── fragments/
│       │   └── UserFields.gql
│       └── GetUser.gql
# In GetUser.gql:
#import "fragments/UserFields.gql"

Unicode Support

GqlCase fully supports Unicode in GraphQL documents:

load_gql_string """
query GetGreeting($name: String!) {
  greeting(name: $name)
}
"""

result = query_gql(variables: %{name: "José"})

Security Constraints

Built-in security measures include:

  • File size limits - Query strings limited to 10MB
  • Null byte detection - Prevents null byte injection
  • Path validation - Ensures safe file path resolution

API Reference

Macros

load_gql_file(file_path)

Loads a GraphQL document from a file path relative to the calling module.

  • Arguments: file_path (string) - Path to the GraphQL file
  • Raises: LoaderError if file cannot be read, ParseError if GraphQL is invalid

load_gql_string(query_string)

Loads a GraphQL document from an inline string.

  • Arguments: query_string (string) - GraphQL query/mutation string
  • Raises: ParseError if GraphQL is invalid

query_gql(opts \\ [])

Executes the loaded GraphQL document against the configured endpoint.

  • Options:
    • variables - Map of GraphQL variables (default: %{})
    • current_user - User map for JWT authentication (default: nil)
    • headers - Additional request headers (default: [])
  • Returns: Decoded JSON response from GraphQL endpoint

Error Types

GqlCase.SetupError

Raised when GqlCase is configured incorrectly:

  • :missing_path - No gql_path provided
  • :missing_jwt_bearer_fn - No JWT function provided
  • :invalid_jwt_bearer_fn - JWT function has wrong arity
  • :double_declaration - Multiple load_gql_* calls in same module

GqlCase.GqlLoader.LoaderError

Raised when files cannot be loaded:

  • Contains path and reason for debugging

GqlCase.GqlLoader.ImportError

Raised when imported files cannot be found:

  • Contains path and parent file information

GqlCase.GqlLoader.ParseError

Raised when GraphQL documents are invalid:

  • Contains path, error message, and line number

For more examples and advanced usage patterns, see the test suite in this repository.

About

GqlCase provides macros to easily test Graphql queries for projects using Absinthe.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages