Skip to main content
Testing is essential when building and maintaining an authorization system. Permify provides a structured way to test your schema (authorization model) together with sample data and expected access check outcomes — all in a single YAML file.
This guide assumes you are familiar with modeling authorization and syncing data in Permify.

How testing works

Permify validation uses a YAML file with three sections:
SectionPurpose
schemaThe authorization model to test
relationshipsSample authorization data (relational tuples)
scenariosTest cases: access checks, entity filters, and subject filters
You can run validation:
  • Locally using the permify validate CLI command.
  • In CI using the permify-validate-action GitHub Action.
  • Coverage analysis using permify coverage.

Schema validation file

Here is a complete example:
schema: >-
  entity user {}

  entity organization {

      relation admin @user
      relation member @user

      action create_repository = (admin or member)
      action delete = admin
  }

  entity repository {

      relation owner @user @organization#member
      relation parent @organization

      action push = owner
      action read = (owner and (parent.admin and parent.member))
      action delete = (parent.member and (parent.admin or owner))
      action edit = parent.member not owner
  }

relationships:
  - "organization:1#admin@user:1"
  - "organization:1#member@user:1"
  - "repository:1#owner@user:1"
  - "repository:2#owner@user:2"
  - "repository:2#owner@user:3"
  - "repository:1#parent@organization:1#..."
  - "organization:1#member@user:43"
  - "repository:1#owner@user:43"

scenarios:
  - name: "scenario 1"
    description: "test description"
    checks:
      - entity: "repository:1"
        subject: "user:1"
        assertions:
          push: true
          owner: true
      - entity: "repository:2"
        subject: "user:1"
        assertions:
          push: false
      - entity: "repository:3"
        subject: "user:1"
        context:
          - "repository:3#owner@user:1"
        assertions:
          push: true
      - entity: "repository:1"
        subject: "user:43"
        assertions:
          edit: false
    entity_filters:
      - entity_type: "repository"
        subject: "user:1"
        context:
          - "repository:3#owner@user:1"
          - "repository:4#owner@user:1"
          - "repository:5#owner@user:1"
        assertions:
          push: ["1", "3", "4", "5"]
          edit: []
    subject_filters:
      - subject_reference: "user"
        entity: "repository:1"
        context:
          - "organization:1#member@user:58"
        assertions:
          push: ["1", "43"]
          edit: ["58"]

Defining the schema

You can embed the schema inline or reference an external file or URL:
schema: >-
  entity user {}
  entity organization {
    ...
  }

Creating test scenarios

Scenarios are test cases grouped under a name and description. Each scenario can contain access checks, entity filters, and subject filters.
scenarios:
  - name: # name of the scenario
    description: # description of the scenario
    checks: # simple access check cases
    entity_filters: # lookup-entity queries
    subject_filters: # lookup-subject queries

Access checks

Use checks to test whether a subject can perform an action on an entity:
checks:
  - entity: "repository:3"      # resource to check access for
    subject: "user:1"            # subject performing the check
    depth: 100                   # traversal depth (default: 100, minimum: 3)
    context:                     # contextual tuples evaluated alongside stored data
      - "repository:3#owner@user:1"
    assertions:                  # expected results for each action
      push: true
The context field lets you inject additional relational tuples that are evaluated alongside your stored data for that specific check — useful for dynamic scenarios involving time, IP address, or other runtime conditions. Depth configuration: The depth parameter controls how deeply the permission engine traverses the relationship graph:
SettingBehavior
0 or omittedDefaults to 100
Minimum allowed3
Recommended for shallow hierarchies35
Recommended for deep hierarchies2050
If you encounter “depth not enough” errors, increase the depth for that check.

Entity filtering

Use entity_filters to test lookup-entity queries — which resources of a given type can a subject access?
entity_filters:
  - entity_type: "repository"   # type of entity to filter
    subject: "user:1"           # subject performing the lookup
    depth: 100
    context:
      - "repository:3#owner@user:1"
      - "repository:4#owner@user:1"
      - "repository:5#owner@user:1"
    assertions:
      push: ["1", "3", "4", "5"]  # expected entity IDs
      edit: []
Unlike access checks, assertions for entity filters specify the IDs of entities expected to be returned, not a boolean.

Subject filtering

Use subject_filters to test lookup-subject queries — which subjects can perform an action on a given entity?
subject_filters:
  - subject_reference: "user"   # type of subject to filter
    entity: "repository:1"      # entity to check access on
    depth: 100
    context:
      - "organization:1#member@user:58"
    assertions:
      push: ["1", "43"]          # expected subject IDs
      edit: ["58"]

Running tests locally

To validate a schema file locally, build Permify and run:
make build
./permify validate {path-to-your-validation-file.yaml}
A successful run prints a validation summary in the terminal. Failures show which assertions did not match the expected results.

Running tests in CI with GitHub Actions

Add the permify-validate-action to your GitHub Actions workflow:
steps:
  - uses: "permify/permify-validate-action@v1.0.0"
    with:
      validationFile: "test.yaml"

Coverage analysis

Measure how well your test file covers your schema:
permify coverage {path-to-your-validation-file.yaml}
Coverage is calculated by analyzing which relationships and assertions in your schema are exercised by the validation file. Missing coverage highlights untested parts of your authorization model.

AST conversion

Convert your schema to an Abstract Syntax Tree (AST) for structural inspection or tooling integration:
permify ast {path-to-your-validation-file.yaml}
This confirms your schema is syntactically valid and can be consumed by downstream tools.

Best practices

  • Store your schema in a version-controlled repository (e.g. Git) and require code review for all changes.
  • Run the schema validator in your CI pipeline on every pull request using the GitHub Action.
  • Use Permify’s in-memory database in unit tests so each test starts with a clean, isolated state.
  • Write scenarios that cover both positive checks (access should be granted) and negative checks (access should be denied).
  • Include entity_filters and subject_filters scenarios to test your lookup queries, not just point-in-time access checks.
See the Authorization Service overview for guidance on managing schema changes in production.

Build docs developers (and LLMs) love