Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 4, 2025

EnforcerServer uses shared global state (ETS table + Registry) for named enforcers. Tests using the same enforcer name interfere with each other when running async: true, causing race conditions where policies added by one test are deleted by another's cleanup.

Changes

Core Module: Acx.TestHelper

  • unique_enforcer_name/0,1 - Generates unique enforcer names per test using erlang:unique_integer/1
  • cleanup_enforcer/1 - Cleans up enforcer from Registry and ETS table
  • setup_enforcer/1,2 - Convenience setup with automatic cleanup via on_exit

Documentation

  • guides/async_testing.md - Comprehensive guide covering three solutions: TestHelper, disabling async, or using Enforcer directly
  • README updated with testing section and example
  • examples/async_test_example.exs - Demonstrates usage patterns

Tests

  • test/test_helper_test.exs - Validates name generation, cleanup, and async isolation

Usage

Before (shared state, race conditions):

defmodule MyTest do
  use ExUnit.Case, async: true
  @enforcer "my_enforcer"  # All tests share this
  
  test "some test" do
    EnforcerServer.add_policy(@enforcer, policy)
    # Another test's cleanup may delete this mid-test
  end
end

After (isolated per test):

defmodule MyTest do
  use ExUnit.Case, async: true
  import Acx.TestHelper
  
  setup do
    setup_enforcer("config.conf")  # Unique enforcer per test
  end
  
  test "some test", %{enforcer_name: ename} do
    EnforcerServer.add_policy(ename, policy)
    # Fully isolated, automatic cleanup
  end
end

Backward compatible - existing synchronous tests unaffected.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • esm.ubuntu.com
    • Triggering command: /usr/lib/apt/methods/https (dns block)
  • repo.hex.pm
    • Triggering command: /usr/lib/erlang/erts-13.2.2.5/bin/inet_gethost 4 (dns block)
    • Triggering command: ping -c 2 repo.hex.pm (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>Shared Global Enforcer state breaks Async tests</issue_title>
<issue_description>## Problem
When using EnforcerServer with a named enforcer (e.g., "reach_enforcer"), all tests share the same global state, making async: true tests fail with race conditions.

What Happened

# This fails with async: true
defmodule Reach.Casbin.AclTest do
  use Reach.CasbinCase, async: true  # ❌ Tests interfere with each other
  
  test "admin has permissions" do
    create_test_policies(test_org_id)  # Adds policies
    # Another test's cleanup deletes these policies mid-test!
    assert Acl.allow?(...) # Returns false unexpectedly
  end
end

Symptoms

  • Policies.list() returns [] even after adding policies
  • add_policy returns {:error, :already_existed} but policies aren't in memory
  • Tests pass individually but fail when run together

Root Cause

# All tests use the same enforcer instance
@enforcer_name "reach_enforcer"
EnforcerServer.add_policy(@enforcer_name, ...)

One test's on_exit cleanup deletes policies while another test is running.

Workaround

# Must disable async
defmodule Reach.Casbin.AclTest do
  use Reach.CasbinCase  # Removed: async: true
end

Proposed Solutions

  1. Support dynamic enforcer names per test
  2. Provide enforcer isolation/sandboxing for tests
  3. Document the async limitation</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@hsluoyz hsluoyz marked this pull request as ready for review November 4, 2025 09:07
Copilot AI changed the title [WIP] Fix shared global enforcer state in async tests Add TestHelper module for async test isolation with EnforcerServer Nov 4, 2025
Copilot AI requested a review from hsluoyz November 4, 2025 09:26
Copilot finished work on behalf of hsluoyz November 4, 2025 09:26
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.

Shared Global Enforcer state breaks Async tests

3 participants