Savvi Studio

Common Testing Scenarios

Practical examples of common testing scenarios using factories and utilities

Table of Contents

Introduction

This guide provides complete, working examples of common testing scenarios. Each scenario includes:

  • Setup - Creating necessary test data
  • Action - Performing the operation being tested
  • Verification - Asserting expected outcomes
  • Cleanup - Removing test data

Copy these examples and adapt them to your needs.

User & Organization Management

Scenario 1: Creating a User with Organization

Use Case: Test user creation with organization membership

import { describe, expect } from 'vitest';
import test from '../test';
import { generateTestId } from '@/test-utils-integration/utils/graph-fixtures';
import { createUserWithOrgSetup } from '../setup/factories/database-fixtures';
import { queryUserAccessCache, refreshUserAccessCache } from '../setup/utilities';

describe('User Creation', () => {
  test('creates user with organization membership', async ({ asSystem }) => {
    await asSystem(async (client) => {
      // Setup: Generate unique IDs
      const userId = generateTestId('user');
      const orgId = generateTestId('org');
      
      // Action: Create user with org
      const { user, org } = await createUserWithOrgSetup(client, {
        userId,
        orgId,
        userData: {
          email: 'john.doe@example.com',
          first_name: 'John',
          last_name: 'Doe',
          role: 'developer'
        },
        orgData: {
          name: 'Acme Corporation',
          domains: ['acme.com'],
          plan: 'enterprise'
        }
      });
      
      // Verify: User created correctly
      expect(user.id).toBe(userId);
      expect(user.nodeId).toBeTruthy();
      expect(user.data.email).toBe('john.doe@example.com');
      
      // Verify: Organization created correctly
      expect(org.id).toBe(orgId);
      expect(org.nodeId).toBeTruthy();
      expect(org.data.name).toBe('Acme Corporation');
      
      // Verify: User has access to organization
      await refreshUserAccessCache(client, userId);
      const access = await queryUserAccessCache(client, userId, org.nodeId);
      expect(access.length).toBeGreaterThan(0);
    });
  });
});

Scenario 2: Creating User with Team

Use Case: Test organizational hierarchy with teams

test('creates user with organization and team', async ({ asSystem }) => {
  await asSystem(async (client) => {
    // Action: Create complete hierarchy
    const { user, org, team } = await createUserWithOrgSetup(client, {
      includeTeam: true,
      teamName: 'Engineering Team',
      userData: {
        email: 'engineer@example.com',
        role: 'engineer'
      }
    });
    
    // Verify: All entities created
    expect(user).toBeDefined();
    expect(org).toBeDefined();
    expect(team).toBeDefined();
    
    // Verify: Team linked to organization
    expect(team!.data.org_id).toBe(org.id);
    expect(team!.data.name).toBe('Engineering Team');
    
    // Verify: User has access through hierarchy
    await refreshUserAccessCache(client, user.id);
    const orgAccess = await queryUserAccessCache(client, user.id, org.nodeId);
    const teamAccess = await queryUserAccessCache(client, user.id, team!.nodeId);
    
    expect(orgAccess.length).toBeGreaterThan(0);
    expect(teamAccess.length).toBeGreaterThan(0);
  });
});

Scenario 3: Multiple Users in Same Organization

Use Case: Test multi-user organization membership

test('creates multiple users in same organization', async ({ asSystem }) => {
  await asSystem(async (client) => {
    const orgId = generateTestId('org');
    
    // Create first user with org
    const { user: user1, org } = await createUserWithOrgSetup(client, {
      orgId,
      userData: { email: 'user1@example.com', role: 'admin' }
    });
    
    // Create second user in same org
    const { user: user2 } = await createUserWithOrgSetup(client, {
      orgId, // Same org
      userData: { email: 'user2@example.com', role: 'member' }
    });
    
    // Verify: Both users in same organization
    await refreshUserAccessCache(client, user1.id);
    await refreshUserAccessCache(client, user2.id);
    
    const user1Access = await queryUserAccessCache(client, user1.id, org.nodeId);
    const user2Access = await queryUserAccessCache(client, user2.id, org.nodeId);
    
    expect(user1Access.length).toBeGreaterThan(0);
    expect(user2Access.length).toBeGreaterThan(0);
  });
});

Permission & Access Control

Scenario 4: Granting Read Permission

Use Case: Test basic permission grant

import { createResourceWithPermission } from '../setup/factories/database-fixtures';
import { checkUserHasAccess } from '../setup/utilities';

test('grants read permission to user', async ({ asSystem, asUser }) => {
  let userId: string;
  let orgId: string;
  let resourceId: string;
  
  // Setup: Create user and resource
  await asSystem(async (client) => {
    const { user } = await createUserWithOrgSetup(client);
    userId = user.id;
    orgId = 'org_id'; // Get from user setup
    
    const { resourceNodeId } = await createResourceWithPermission(client, {
      userNodeId: user.nodeId,
      resourceType: 'test.document',
      permissionLevel: 'read',
      resourceData: {
        title: 'Public Document',
        status: 'published'
      }
    });
    resourceId = resourceNodeId;
    
    await refreshUserAccessCache(client, userId);
  });
  
  // Verify: User can access resource
  await asUser(userId, orgId, async (client) => {
    const hasAccess = await checkUserHasAccess(client, resourceId);
    expect(hasAccess).toBe(true);
  });
});

Scenario 5: Permission Levels (Read, Write, Admin)

Use Case: Test different permission levels

test('verifies permission hierarchy', async ({ asSystem }) => {
  await asSystem(async (client) => {
    const { user } = await createUserWithOrgSetup(client);
    
    // Create resources with different permissions
    const { resourceNodeId: readResource } = await createResourceWithPermission(
      client,
      {
        userNodeId: user.nodeId,
        permissionLevel: 'read',
        resourceData: { name: 'Read Only' }
      }
    );
    
    const { resourceNodeId: writeResource } = await createResourceWithPermission(
      client,
      {
        userNodeId: user.nodeId,
        permissionLevel: 'write',
        resourceData: { name: 'Editable' }
      }
    );
    
    const { resourceNodeId: adminResource } = await createResourceWithPermission(
      client,
      {
        userNodeId: user.nodeId,
        permissionLevel: 'admin',
        resourceData: { name: 'Full Control' }
      }
    );
    
    await refreshUserAccessCache(client, user.id);
    
    // Verify: Read permission
    const readAccess = await queryUserAccessCache(client, user.id, readResource);
    expect(readAccess[0]?.min_permission).toBe('auth.read');
    
    // Verify: Write permission
    const writeAccess = await queryUserAccessCache(client, user.id, writeResource);
    expect(writeAccess[0]?.min_permission).toMatch(/auth\.read\.write/);
    
    // Verify: Admin permission
    const adminAccess = await queryUserAccessCache(client, user.id, adminResource);
    expect(adminAccess[0]?.min_permission).toBe('auth.read.write.admin');
  });
});

Scenario 6: Revoking Permissions

Use Case: Test permission removal

import { deleteEdge } from '../setup/utilities';

test('revokes user permission', async ({ asSystem, asUser }) => {
  let userId: string;
  let orgId: string;
  let resourceId: string;
  let permissionId: string;
  
  // Setup: Grant permission
  await asSystem(async (client) => {
    const { user, org } = await createUserWithOrgSetup(client);
    userId = user.id;
    orgId = org.id;
    
    const result = await createResourceWithPermission(client, {
      userNodeId: user.nodeId,
      permissionLevel: 'write'
    });
    resourceId = result.resourceNodeId;
    permissionId = result.permissionId;
    
    await refreshUserAccessCache(client, userId);
  });
  
  // Verify: Initially has access
  await asUser(userId, orgId, async (client) => {
    const hasAccess = await checkUserHasAccess(client, resourceId);
    expect(hasAccess).toBe(true);
  });
  
  // Action: Revoke permission
  await asSystem(async (client) => {
    await deleteEdge(client, permissionId);
    await refreshUserAccessCache(client, userId);
  });
  
  // Verify: No longer has access
  await asUser(userId, orgId, async (client) => {
    const hasAccess = await checkUserHasAccess(client, resourceId);
    expect(hasAccess).toBe(false);
  });
});

Graph Operations

Scenario 7: Creating Node Relationships

Use Case: Test graph edge creation

import { createConnectedNodes } from '../setup/factories/database-fixtures';
import { getEdgesBetweenNodes } from '../setup/utilities';

test('creates connected nodes', async ({ asSystem }) => {
  await asSystem(async (client) => {
    // Action: Create person → company relationship
    const { source: person, target: company, edge } = await createConnectedNodes(
      client,
      {
        sourceType: 'test.person',
        targetType: 'test.company',
        edgeType: 'test.works_at',
        sourceData: {
          name: 'Alice Johnson',
          title: 'Senior Engineer',
          skills: ['TypeScript', 'React', 'Node.js']
        },
        targetData: {
          name: 'Acme Corp',
          industry: 'Software',
          size: 'enterprise'
        },
        edgeData: {
          start_date: '2024-01-01',
          employment_type: 'full-time'
        }
      }
    );
    
    // Verify: Nodes created
    expect(person.nodeId).toBeTruthy();
    expect(company.nodeId).toBeTruthy();
    
    // Verify: Edge exists
    const edges = await getEdgesBetweenNodes(
      client,
      person.nodeId,
      company.nodeId,
      'test.works_at'
    );
    expect(edges).toHaveLength(1);
    expect(edges[0].data.employment_type).toBe('full-time');
  });
});

Scenario 8: Graph Traversal

Use Case: Test multi-hop relationships

test('traverses multi-level graph', async ({ asSystem }) => {
  await asSystem(async (client) => {
    // Create: Person → Company → Project chain
    const { source: person, target: company } = await createConnectedNodes(client, {
      sourceType: 'test.person',
      targetType: 'test.company',
      edgeType: 'test.works_at',
      sourceData: { name: 'Alice' },
      targetData: { name: 'Acme Corp' }
    });
    
    const { target: project } = await createConnectedNodes(client, {
      sourceType: 'test.company',
      targetType: 'test.project',
      edgeType: 'test.owns',
      sourceData: { id: company.id },
      targetData: { name: 'Project X', status: 'active' }
    });
    
    // Verify: All relationships exist
    const companyEdges = await getEdgesBetweenNodes(
      client,
      person.nodeId,
      company.nodeId
    );
    expect(companyEdges).toHaveLength(1);
    
    const projectEdges = await getEdgesBetweenNodes(
      client,
      company.nodeId,
      project.nodeId
    );
    expect(projectEdges).toHaveLength(1);
  });
});

Scenario 9: Querying Nodes by Type

Use Case: Find all nodes of a specific type

import { getNodesByType } from '../setup/utilities';

test('queries nodes by type', async ({ asSystem }) => {
  await asSystem(async (client) => {
    // Create multiple resources
    const workspace = await setupTestWorkspace(client, {
      resourceCount: 5,
      resourceType: 'test.document'
    });
    
    // Query all documents
    const documents = await getNodesByType(client, 'test.document');
    
    // Verify: All documents found
    expect(documents.length).toBeGreaterThanOrEqual(5);
    
    // Verify: Each has correct type
    documents.forEach(doc => {
      expect(doc.node_type_id).toBe('test.document');
    });
  });
});

WorkOS Webhook Processing

Scenario 10: User Created Webhook

Use Case: Test user.created webhook processing

import { createUserEvent } from '../setup/factories/workos-events';
import { getNodesByType } from '../setup/utilities';

test('processes user.created webhook', async ({ asSystem }) => {
  const userId = 'user_workos_123';
  
  // Action: Create webhook event
  const event = createUserEvent('user.created', userId, {
    email: 'john.doe@example.com',
    firstName: 'John',
    lastName: 'Doe',
    emailVerified: true,
    metadata: { source: 'sso' }
  });
  
  // Process webhook (your handler)
  await handleWorkOSWebhook(event);
  
  // Verify: User created in database
  await asSystem(async (client) => {
    const users = await getNodesByType(client, 'platform.user');
    const user = users.find(u => u.external_id === userId);
    
    expect(user).toBeDefined();
    expect(user.data.email).toBe('john.doe@example.com');
    expect(user.data.first_name).toBe('John');
    expect(user.data.last_name).toBe('Doe');
    expect(user.data.email_verified).toBe(true);
  });
});

Scenario 11: Organization Created Webhook

Use Case: Test organization.created webhook

import { createOrgEvent } from '../setup/factories/workos-events';

test('processes organization.created webhook', async ({ asSystem }) => {
  const orgId = 'org_workos_456';
  
  // Action: Create webhook event
  const event = createOrgEvent('organization.created', orgId, {
    name: 'Acme Corporation',
    domains: [
      { domain: 'acme.com', state: 'verified' },
      { domain: 'acmecorp.com', state: 'verified' }
    ],
    allowProfilesOutsideOrganization: false,
    metadata: { tier: 'enterprise' }
  });
  
  await handleWorkOSWebhook(event);
  
  // Verify: Organization created
  await asSystem(async (client) => {
    const orgs = await getNodesByType(client, 'platform.organization');
    const org = orgs.find(o => o.external_id === orgId);
    
    expect(org).toBeDefined();
    expect(org.data.name).toBe('Acme Corporation');
    expect(org.data.domains).toHaveLength(2);
    expect(org.data.allow_profiles_outside_organization).toBe(false);
  });
});

Scenario 12: Complete User Onboarding Flow

Use Case: Test complete webhook sequence

import {
  createUserEvent,
  createOrgEvent,
  createMembershipEvent
} from '../setup/factories/workos-events';

test('processes complete user onboarding', async ({ asSystem }) => {
  const userId = 'user_789';
  const orgId = 'org_789';
  const membershipId = 'membership_789';
  
  // Step 1: Organization created
  const orgEvent = createOrgEvent('organization.created', orgId, {
    name: 'Tech Startup Inc',
    domains: [{ domain: 'techstartup.com' }]
  });
  await handleWorkOSWebhook(orgEvent);
  
  // Step 2: User created
  const userEvent = createUserEvent('user.created', userId, {
    email: 'founder@techstartup.com',
    firstName: 'Jane',
    lastName: 'Founder'
  });
  await handleWorkOSWebhook(userEvent);
  
  // Step 3: Membership created
  const membershipEvent = createMembershipEvent(
    'organization_membership.created',
    membershipId,
    userId,
    orgId,
    {
      role: { slug: 'admin' },
      status: 'active'
    }
  );
  await handleWorkOSWebhook(membershipEvent);
  
  // Verify: Complete state
  await asSystem(async (client) => {
    // Verify user
    const users = await getNodesByType(client, 'platform.user');
    const user = users.find(u => u.external_id === userId);
    expect(user).toBeDefined();
    
    // Verify org
    const orgs = await getNodesByType(client, 'platform.organization');
    const org = orgs.find(o => o.external_id === orgId);
    expect(org).toBeDefined();
    
    // Verify membership edge
    const edges = await getEdgesBetweenNodes(
      client,
      user.id,
      org.id,
      'auth.read.member'
    );
    expect(edges).toHaveLength(1);
    
    // Verify user access
    await refreshUserAccessCache(client, userId);
    const access = await queryUserAccessCache(client, userId, org.id);
    expect(access.length).toBeGreaterThan(0);
  });
});

Transitive Permissions

Scenario 13: Inherited Organization Permissions

Use Case: Test permission inheritance through organization

test('inherits permissions through organization', async ({ asSystem }) => {
  await asSystem(async (client) => {
    // Create user → org → resource hierarchy
    const { user, org } = await createUserWithOrgSetup(client);
    
    // Grant permission to organization (not user directly)
    const { resourceNodeId } = await createResourceWithPermission(client, {
      userNodeId: org.nodeId, // Grant to org, not user
      resourceType: 'test.company_resource',
      permissionLevel: 'read',
      resourceData: { shared_with: 'all_org_members' }
    });
    
    // Refresh cache to pick up transitive permissions
    await refreshUserAccessCache(client, user.id);
    
    // Verify: User has access through org membership
    const access = await queryUserAccessCache(client, user.id, resourceNodeId);
    expect(access.length).toBeGreaterThan(0);
    
    // Verify: Permission is transitive (depth > 0)
    expect(access[0].depth).toBeGreaterThan(0);
    expect(access[0].min_permission).toBe('auth.read');
  });
});

Scenario 14: Team-Based Permissions

Use Case: Test permission inheritance through team

test('inherits permissions through team', async ({ asSystem }) => {
  await asSystem(async (client) => {
    // Create user → team → org hierarchy
    const { user, org, team } = await createUserWithOrgSetup(client, {
      includeTeam: true,
      teamName: 'Engineering'
    });
    
    // Grant permission to team
    const { resourceNodeId } = await createResourceWithPermission(client, {
      userNodeId: team!.nodeId,
      resourceType: 'test.team_project',
      permissionLevel: 'write',
      resourceData: { team_name: 'Engineering' }
    });
    
    await refreshUserAccessCache(client, user.id);
    
    // Verify: User has access through team membership
    const access = await queryUserAccessCache(client, user.id, resourceNodeId);
    expect(access.length).toBeGreaterThan(0);
    expect(access[0].min_permission).toMatch(/auth\.read\.write/);
  });
});

Scenario 15: Multi-Level Permission Chain

Use Case: Test deep permission hierarchies

test('follows multi-level permission chain', async ({ asSystem }) => {
  await asSystem(async (client) => {
    // Create: User → Team → Org → Department → Project
    const { user, org, team } = await createUserWithOrgSetup(client, {
      includeTeam: true
    });
    
    // Create department node
    const department = await createNodeWithType(client, 'test.department', {
      name: 'Engineering Department',
      org_id: org.id
    });
    
    // Link org → department
    await createConnectedNodes(client, {
      sourceType: 'platform.organization',
      targetType: 'test.department',
      edgeType: 'auth.read.member',
      sourceData: { id: org.id },
      targetData: { id: department.id }
    });
    
    // Create project owned by department
    const { resourceNodeId: projectId } = await createResourceWithPermission(client, {
      userNodeId: department.nodeId,
      resourceType: 'test.project',
      permissionLevel: 'admin'
    });
    
    await refreshUserAccessCache(client, user.id);
    
    // Verify: User has access through entire chain
    const access = await queryUserAccessCache(client, user.id, projectId);
    expect(access.length).toBeGreaterThan(0);
    expect(access[0].depth).toBeGreaterThan(1); // Multi-hop
  });
});

RLS Policy Testing

Scenario 16: Row-Level Security Enforcement

Use Case: Test RLS policies prevent unauthorized access

test('enforces RLS policies', async ({ asSystem, asUser }) => {
  let user1Id: string, user1NodeId: string;
  let user2Id: string, user2NodeId: string;
  let privateResourceId: string;
  
  // Setup: Create two users and a private resource
  await asSystem(async (client) => {
    const { user: user1 } = await createUserWithOrgSetup(client);
    const { user: user2 } = await createUserWithOrgSetup(client);
    
    user1Id = user1.id;
    user1NodeId = user1.nodeId;
    user2Id = user2.id;
    user2NodeId = user2.nodeId;
    
    // Create private resource for user1
    const { resourceNodeId } = await createResourceWithPermission(client, {
      userNodeId: user1NodeId,
      resourceType: 'test.private_document',
      permissionLevel: 'admin',
      resourceData: { classification: 'confidential' }
    });
    privateResourceId = resourceNodeId;
    
    await refreshUserAccessCache(client, user1Id);
    await refreshUserAccessCache(client, user2Id);
  });
  
  // Test: User1 can access
  await asUser(user1Id, 'org1', async (client) => {
    const hasAccess = await checkUserHasAccess(client, privateResourceId);
    expect(hasAccess).toBe(true);
  });
  
  // Test: User2 cannot access
  await asUser(user2Id, 'org2', async (client) => {
    const hasAccess = await checkUserHasAccess(client, privateResourceId);
    expect(hasAccess).toBe(false);
  });
});

Scenario 17: Cross-Organization Isolation

Use Case: Ensure organizations cannot see each other's data

test('isolates data between organizations', async ({ asSystem }) => {
  await asSystem(async (client) => {
    // Create two separate organizations
    const { user: user1, org: org1 } = await createUserWithOrgSetup(client, {
      orgData: { name: 'Company A' }
    });
    
    const { user: user2, org: org2 } = await createUserWithOrgSetup(client, {
      orgData: { name: 'Company B' }
    });
    
    // Create org-specific resources
    const { resourceNodeId: org1Resource } = await createResourceWithPermission(
      client,
      {
        userNodeId: org1.nodeId,
        resourceData: { owner: 'Company A' }
      }
    );
    
    const { resourceNodeId: org2Resource } = await createResourceWithPermission(
      client,
      {
        userNodeId: org2.nodeId,
        resourceData: { owner: 'Company B' }
      }
    );
    
    await refreshUserAccessCache(client, user1.id);
    await refreshUserAccessCache(client, user2.id);
    
    // Verify: User1 can only see org1 resources
    const user1Access = await queryUserAccessCache(client, user1.id);
    const user1CanSeeOrg2 = user1Access.some(a => a.node_id === org2Resource);
    expect(user1CanSeeOrg2).toBe(false);
    
    // Verify: User2 can only see org2 resources
    const user2Access = await queryUserAccessCache(client, user2.id);
    const user2CanSeeOrg1 = user2Access.some(a => a.node_id === org1Resource);
    expect(user2CanSeeOrg1).toBe(false);
  });
});

Cleanup & Data Isolation

Scenario 18: Cleanup After Test

Use Case: Proper test cleanup to prevent data leakage

import { cleanupTestData } from '../setup/utilities';

test('cleans up test data properly', async ({ asSystem }) => {
  await asSystem(async (client) => {
    try {
      // Create test data
      const workspace = await setupTestWorkspace(client, {
        resourceCount: 10,
        resourceType: 'test.temp_resource'
      });
      
      // Run test operations...
      expect(workspace.resources).toHaveLength(10);
      
    } finally {
      // Always cleanup, even if test fails
      const result = await cleanupTestData(client, {
        nodeTypes: [
          'platform.user',
          'platform.organization',
          'test.temp_resource'
        ],
        edgeTypes: ['auth.read', 'auth.read.member'],
        externalIdPattern: 'test_%'
      });
      
      console.log(`Cleaned up: ${result.nodes} nodes, ${result.edges} edges`);
    }
  });
});

Scenario 19: Pattern-Based Cleanup

Use Case: Clean up by external ID pattern

import { cleanupByExternalIdPattern } from '../setup/utilities';

test('cleans up by pattern', async ({ asSystem }) => {
  await asSystem(async (client) => {
    const testPrefix = `test_${Date.now()}`;
    
    try {
      // Create nodes with specific prefix
      await createUserWithOrgSetup(client, {
        userId: `${testPrefix}_user1`,
        orgId: `${testPrefix}_org1`
      });
      
      await createUserWithOrgSetup(client, {
        userId: `${testPrefix}_user2`,
        orgId: `${testPrefix}_org2`
      });
      
      // Test operations...
      
    } finally {
      // Clean up all nodes matching pattern
      const deleted = await cleanupByExternalIdPattern(
        client,
        `${testPrefix}_%`
      );
      expect(deleted).toBeGreaterThan(0);
    }
  });
});

Error Conditions

Scenario 20: Testing Access Denial

Use Case: Verify operations fail without proper permissions

test('denies access without permission', async ({ asSystem, asUser }) => {
  let userId: string;
  let orgId: string;
  let resourceId: string;
  
  // Setup: Create user and resource (no permission)
  await asSystem(async (client) => {
    const { user, org } = await createUserWithOrgSetup(client);
    userId = user.id;
    orgId = org.id;
    
    // Create resource WITHOUT granting permission to user
    const resource = await createNodeWithType(client, 'test.protected', {
      classification: 'top-secret'
    });
    resourceId = resource.nodeId;
    
    await refreshUserAccessCache(client, userId);
  });
  
  // Verify: User cannot access
  await asUser(userId, orgId, async (client) => {
    const hasAccess = await checkUserHasAccess(client, resourceId);
    expect(hasAccess).toBe(false);
    
    // Attempting to read should fail
    await expect(
      client.query('SELECT * FROM graph.nodes WHERE id = $1', [resourceId])
    ).rejects.toThrow(); // RLS should block
  });
});

Performance Testing

Scenario 21: Bulk Resource Creation

Use Case: Test performance with many resources

import { setupTestWorkspace } from '../setup/factories/database-fixtures';

test('handles bulk resource creation', async ({ asSystem }) => {
  const startTime = Date.now();
  
  await asSystem(async (client) => {
    // Create workspace with 100 resources
    const workspace = await setupTestWorkspace(client, {
      resourceCount: 100,
      resourceType: 'test.bulk_resource',
      resourcePermission: 'read'
    });
    
    expect(workspace.resources).toHaveLength(100);
    
    // Verify cache updated
    await refreshUserAccessCache(client, workspace.user.id);
    const access = await queryUserAccessCache(client, workspace.user.id);
    
    // Should have access to all resources
    expect(access.length).toBeGreaterThanOrEqual(100);
    
    // Cleanup
    await cleanupTestData(client, {
      nodeTypes: ['test.bulk_resource', 'platform.user', 'platform.organization'],
      externalIdPattern: 'test_%'
    });
  });
  
  const duration = Date.now() - startTime;
  console.log(`Bulk creation took ${duration}ms`);
  
  // Should complete in reasonable time
  expect(duration).toBeLessThan(30000); // 30 seconds
});

Scenario 22: Cache Performance

Use Case: Test access cache query performance

test('queries cache efficiently with many permissions', async ({ asSystem }) => {
  await asSystem(async (client) => {
    // Create user with many permissions
    const workspace = await setupTestWorkspace(client, {
      resourceCount: 50,
      resourcePermission: 'admin'
    });
    
    await refreshUserAccessCache(client, workspace.user.id);
    
    // Measure query performance
    const startTime = Date.now();
    const access = await queryUserAccessCache(client, workspace.user.id);
    const duration = Date.now() - startTime;
    
    expect(access.length).toBeGreaterThanOrEqual(50);
    expect(duration).toBeLessThan(1000); // Should be fast
    
    console.log(`Cache query returned ${access.length} records in ${duration}ms`);
  });
});

Scenario 23: Concurrent Operations

Use Case: Test handling of concurrent permission grants

test('handles concurrent permission grants', async ({ asSystem }) => {
  await asSystem(async (client) => {
    const { user } = await createUserWithOrgSetup(client);
    
    // Create multiple resources concurrently
    const promises = Array.from({ length: 20 }, (_, i) =>
      createResourceWithPermission(client, {
        userNodeId: user.nodeId,
        resourceType: 'test.concurrent',
        permissionLevel: 'read',
        resourceData: { index: i }
      })
    );
    
    const startTime = Date.now();
    const results = await Promise.all(promises);
    const duration = Date.now() - startTime;
    
    expect(results).toHaveLength(20);
    console.log(`Concurrent creation took ${duration}ms`);
    
    // Verify all permissions granted
    await refreshUserAccessCache(client, user.id);
    const access = await queryUserAccessCache(client, user.id);
    expect(access.length).toBeGreaterThanOrEqual(20);
  });
});

Summary

This guide has covered 23 common testing scenarios:

User & Organization Management (3 scenarios)

  • Creating users with organizations
  • Team hierarchies
  • Multi-user organizations

Permission & Access Control (3 scenarios)

  • Granting permissions
  • Permission levels
  • Revoking permissions

Graph Operations (3 scenarios)

  • Node relationships
  • Graph traversal
  • Querying by type

WorkOS Webhook Processing (3 scenarios)

  • User webhooks
  • Organization webhooks
  • Complete onboarding flows

Transitive Permissions (3 scenarios)

  • Organization inheritance
  • Team-based permissions
  • Multi-level chains

RLS Policy Testing (2 scenarios)

  • Row-level security
  • Cross-organization isolation

Cleanup & Data Isolation (2 scenarios)

  • Proper cleanup
  • Pattern-based cleanup

Error Conditions (1 scenario)

  • Access denial

Performance Testing (3 scenarios)

  • Bulk operations
  • Cache performance
  • Concurrent operations

Next Steps

  • Adapt these scenarios to your specific use cases
  • Combine patterns to create complex test scenarios
  • Use as templates for new tests
  • Reference when migrating existing tests

Questions? Check the Troubleshooting Guide or use /reportbug