Permissions Roles, permissions, and policies for NetPress
NetPressv0.1.7 Permissionsv0.2.3 Docsv0.2.2
Overview Installation Policies Auth Guard
Reference

Testing

The package ships its own Jest suite and is designed to be straightforward to test inside consuming applications. This page walks through patterns for keeping your tests isolated and deterministic.

Built-In Suite

cd packages/netpress-permissions
npm test

Under the hood this runs:

NODE_OPTIONS='--experimental-vm-modules --import tsx' \
  jest --config ./jest.config.js --runInBand --detectOpenHandles

The suite covers:

  • NetpressPermissionsTest.js — role/permission graph and morph lookups
  • AuthGuardTest.js — Express middleware (401 / 403 behaviour, policy + permission checks)
  • PublisherTest.js — vendor:publish flow and provider hydration

Isolating State Between Tests

Authorization carries a lot of shared global state. Reset it in every test teardown:

import {
  resetConnectionRegistry,
  resetMorphMap,
  resetAuthGuards,
  resetPolicies,
  container,
} from '@admicaa/netpress';
import {
  resetPermissionsConfig,
} from '@admicaa/netpress-permissions';

afterEach(() => {
  resetPermissionsConfig();
  resetAuthGuards();
  resetPolicies();
  resetMorphMap();
  resetConnectionRegistry();
  container.reset();
});

A Minimal Integration Test

import knexLib from 'knex';
import { BaseModel, bindActiveConnection } from '@admicaa/netpress';
import {
  Authorizable,
  Permission,
  Role,
  createPermissionTables,
} from '@admicaa/netpress-permissions';

class User extends Authorizable(BaseModel) {
  static table = 'users';
}

let knex;

beforeEach(async () => {
  knex = knexLib({
    client: 'better-sqlite3',
    connection: { filename: ':memory:' },
    useNullAsDefault: true,
  });

  await knex.schema.createTable('users', (table) => {
    table.increments('id').primary();
    table.string('name').notNullable();
  });

  await createPermissionTables(knex);

  bindActiveConnection({ name: 'sqlite', driver: 'sqlite', client: knex, reset: true });
  BaseModel.registerMorphMap({ user: User });
});

afterEach(async () => {
  await knex.destroy();
});

it('grants a permission via a role', async () => {
  const editor = await Role.findOrCreate('editor');
  const publish = await Permission.findOrCreate('posts.publish');
  await editor.givePermissionTo(publish);

  const user = await User.create({ name: 'Amina' });
  await user.assignRole(editor);

  expect(await user.can('posts.publish')).toBe(true);
});

Testing Middleware

Invoke AuthGuard.*() directly without a running Express server:

function invoke(middleware, req = {}) {
  return new Promise((resolve) => middleware(req, {}, resolve));
}

it('rejects guests', async () => {
  const err = await invoke(AuthGuard.authenticated(), {});
  expect(err).toMatchObject({ status: 401 });
});

The next callback receives the thrown exception, which lets Jest assert status codes without running the whole HTTP stack.

Mocking The User

For most tests it is simpler to use Authorizable(BaseModel) against an in-memory database than to mock the user. When you truly need a mock:

const fakeUser = {
  id: 1,
  hasPermissionTo: async () => true,
  hasAnyRole: async () => true,
  hasAllRoles: async () => true,
  getRoleNames: async () => ['admin'],
};

The permission traits and AuthGuard only call the methods above, so a plain object works fine for unit tests that don't care about the persistence layer.