Savvi Studio

Getting Started with Testing

Quick start guide to writing tests in Savvi Studio

Quick Start

# 1. Verify prerequisites
psql --version  # PostgreSQL 14+
node --version  # Node.js 18+

# 2. Run existing tests
pnpm test tests/integration

# 3. Write your first test
# See example below

Prerequisites

  • ✅ PostgreSQL 14+ installed and running
  • ✅ Node.js 18+ and pnpm
  • ✅ Environment configured (.env.test)
  • ✅ Database migrations applied

Full setup guide: Setup Environment Task

Your First Test

Create tests/integration/auth/my-first-test.test.ts:

import { describe, expect } from 'vitest';
import test from '../test';
import { createUserWithOrgSetup } from '../setup/factories/database-fixtures';
import { queryUserAccessCache } from '../setup/utilities';

describe('My First Test', () => {
  test('should create user and verify access', async ({ asSystem }) => {
    await asSystem(async (client) => {
      // Use factory to create test data
      const { user, org } = await createUserWithOrgSetup(client);
      
      // Use utility to query access
      const access = await queryUserAccessCache(client, user.id);
      
      // Verify results
      expect(access.length).toBeGreaterThan(0);
      expect(user.data.email).toContain('@test.com');
    });
  });
});

Run it:

pnpm test tests/integration/auth/my-first-test.test.ts

Core Concepts

1. Test Fixtures

Test fixtures provide execution contexts:

// System privileges (for setup/teardown)
await asSystem(async (client) => { /* ... */ });

// User session context
await asUser(userId, orgId, async (client) => { /* ... */ });

// Admin privileges
await asAdmin(userId, orgId, async (client) => { /* ... */ });

Full API: Test Fixtures Reference

2. Factory Functions

Factories create test data with sensible defaults:

// Create user with organization
const { user, org } = await createUserWithOrgSetup(client);

// Create resource with permission
const { resourceNodeId } = await createResourceWithPermission(client, {
  userNodeId: user.nodeId,
  permissionLevel: 'write'
});

// Create complete workspace
const workspace = await setupTestWorkspace(client, {
  includeTeam: true,
  resourceCount: 3
});

Full API: Factories & Utilities Reference

3. Query Utilities

Utilities provide common query operations:

// Query user access
const access = await queryUserAccessCache(client, userId);

// Check permissions
const hasAccess = await checkUserHasAccess(client, nodeId);
const canManage = await checkUserCanManage(client, nodeId);
const isAdmin = await checkUserIsAdmin(client, nodeId);

// Refresh cache
await refreshUserAccessCache(client, userId);

Full API: Factories & Utilities Reference

Common Commands

# Run all tests
pnpm test

# Run integration tests only
pnpm test tests/integration

# Run specific domain
pnpm test tests/integration/auth

# Watch mode (auto-rerun on changes)
pnpm test -- --watch

# Coverage report
pnpm test -- --coverage

# Debug mode (verbose output)
pnpm test -- --reporter=verbose

# Stop on first failure
pnpm test -- --bail=1

Common Patterns

Pattern 1: Create and Test User Access

test('user access pattern', async ({ asSystem }) => {
  await asSystem(async (client) => {
    // Create test data
    const { user, org } = await createUserWithOrgSetup(client);
    
    const { resourceNodeId } = await createResourceWithPermission(client, {
      userNodeId: user.nodeId,
      permissionLevel: 'write'
    });
    
    // Verify access
    await refreshUserAccessCache(client, user.id);
    const access = await queryUserAccessCache(client, user.id, resourceNodeId);
    
    expect(access.length).toBeGreaterThan(0);
    expect(access[0]?.min_permission).toBe('auth.read.write');
  });
});

Pattern 2: Test as Specific User

test('user context pattern', async ({ asUser, asSystem }) => {
  let userId: string;
  let nodeId: string;
  
  // Setup as system
  await asSystem(async (client) => {
    const { user } = await createUserWithOrgSetup(client);
    userId = user.id;
    
    const { resourceNodeId } = await createResourceWithPermission(client, {
      userNodeId: user.nodeId,
      permissionLevel: 'read'
    });
    nodeId = resourceNodeId;
  });
  
  // Test as user
  await asUser(userId, 'org-id', async (client) => {
    const hasAccess = await checkUserHasAccess(client, nodeId);
    expect(hasAccess).toBe(true);
  });
});

Pattern 3: Complete Workspace Setup

test('workspace pattern', async ({ asSystem }) => {
  await asSystem(async (client) => {
    const workspace = await setupTestWorkspace(client, {
      includeTeam: true,
      resourceCount: 3,
      resourceType: 'test.document',
      resourcePermission: 'write'
    });
    
    expect(workspace.user).toBeDefined();
    expect(workspace.org).toBeDefined();
    expect(workspace.team).toBeDefined();
    expect(workspace.resources).toHaveLength(3);
  });
});

Essential Imports

// Test fixtures
import test from '../test';
import { generateTestId } from '@/test-utils-integration/utils/graph-fixtures';

// Factory functions
import {
  createUserWithOrgSetup,
  createResourceWithPermission,
  setupTestWorkspace
} from '../setup/factories/database-fixtures';

// Query utilities
import {
  queryUserAccessCache,
  refreshUserAccessCache,
  checkUserHasAccess,
  checkUserCanManage,
  checkUserIsAdmin
} from '../setup/utilities';

// Cleanup utilities
import {
  cleanupTestData,
  deleteNodesByType
} from '../setup/utilities/cleanup-helpers';

Troubleshooting

Common Issues

Connection refused:

# PostgreSQL not running
brew services start postgresql  # macOS
sudo systemctl start postgresql # Linux

Database doesn't exist:

createdb savvi_studio_test

Permission denied:

ALTER USER postgres CREATEDB;

More help: Troubleshooting Guide

Next Steps

Learn the Infrastructure

  1. Test Fixtures - Complete fixture API
  2. Factories & Utilities - 42+ functions reference
  3. Database Testing - Database patterns
  4. Integration Testing - Integration patterns

See Examples

  1. Common Scenarios - 23+ practical examples
  2. Examples Directory - Working code examples

Implementation Tasks

Quick Reference

Available Fixtures

Fixture Purpose Example
asSystem System privileges Setup, cleanup, admin operations
asUser User session Testing user permissions
asAdmin Admin privileges Testing admin features
systemClient Direct client Low-level operations
graph Graph operations Testing graph queries
graphAsRoot Root graph ops Graph setup
studio Studio operations Testing studio API
studioAsRoot Root studio ops Studio setup
assertions Assertions Common assertions

Key Factory Functions

  • createUserWithOrgSetup() - User with organization
  • createResourceWithPermission() - Resource with granted permission
  • setupTestWorkspace() - Complete test environment
  • createNodeWithType() - Create node with type
  • createConnectedNodes() - Two connected nodes

Key Query Utilities

  • queryUserAccessCache() - Query access cache
  • refreshUserAccessCache() - Update access cache
  • checkUserHasAccess() - Check read access
  • checkUserCanManage() - Check write access
  • checkUserIsAdmin() - Check admin access
  • getCurrentUserId() - Get session user
  • getCurrentRole() - Get session role

Ready to dive deeper?Integration Testing Guide