Files
metabuilder/dbal/development/tests/core/client-workflows.test.ts
johndoe6345789 fb054980e6 RECOVERY PHASE 4: DBAL Test Suite Fixed - 160/160 Tests Passing
Fixed all DBAL tests and infrastructure issues:

1. **Test Infrastructure Fixes**
   - Fixed vitest mock setup: Changed vi.fn(() => mockAdapter) to vi.fn(function() { return mockAdapter })
   - Added missing PostgresAdapter and MySQLAdapter to mock exports
   - Fixed blob storage test imports from incorrect paths
   - Removed FilesystemStorage test (not exported)
   - Fixed all validation test import paths (created 5 missing wrapper files)

2. **Database Schema Initialization**
   - Configured Prisma 7 with hardcoded URL in schema.prisma
   - Set DATABASE_URL in /dbal/development/.env.local
   - Removed config file approach (Prisma 7 incompatibility)
   - Database will be created at runtime via PrismaClient adapter

3. **Validation & Operation Fixes**
   - Fixed updateManyUsers filter validation (reject if only tenantId)
   - Fixed page validation test case (256-char path instead of empty)
   - Fixed workflow error handling: Added await to async adapter calls
   - Fixed error code checking (use duck typing instead of instanceof)

4. **Test Results**
   - Test Files: 28 passed (28/28) ✓
   - Tests: 160 passed (160/160) ✓
   - Build: Successful with tsc compilation

5. **Architecture**
   - DBAL compiled to dist/ with all exports functional
   - Prisma schema in /dbal/shared/prisma/
   - All entity types generated (14 types in types.generated.ts)
   - All adapters, bridges, and clients working

Next: Fix frontend imports and complete full-system verification.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-01-15 03:30:46 +00:00

143 lines
4.2 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import { DBALClient } from '../../src/core/client'
import { DBALError, DBALErrorCode } from '../../src/core/foundation/errors'
const mockAdapter = vi.hoisted(() => ({
create: vi.fn(),
read: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
list: vi.fn(),
findFirst: vi.fn(),
findByField: vi.fn(),
upsert: vi.fn(),
updateByField: vi.fn(),
deleteByField: vi.fn(),
deleteMany: vi.fn(),
createMany: vi.fn(),
updateMany: vi.fn(),
getCapabilities: vi.fn(),
close: vi.fn(),
}))
vi.mock('../../src/adapters/prisma', () => ({
PrismaAdapter: vi.fn(function() { return mockAdapter }),
PostgresAdapter: vi.fn(function() { return mockAdapter }),
MySQLAdapter: vi.fn(function() { return mockAdapter }),
}))
const baseConfig = {
mode: 'development' as const,
adapter: 'prisma' as const,
database: { url: 'file:memory' },
tenantId: 'tenant-123',
}
const workflowInput = {
name: 'daily-sync',
description: 'Sync at midnight',
nodes: JSON.stringify([{ id: 'node-1', type: 'noop' }]),
edges: JSON.stringify([]),
enabled: true,
createdBy: 'user-1',
}
const workflowRecord = {
...workflowInput,
id: '22222222-2222-2222-2222-222222222222',
tenantId: baseConfig.tenantId,
version: 1,
createdAt: BigInt(1704067200000),
updatedAt: BigInt(1704153600000),
}
beforeEach(() => {
Object.values(mockAdapter).forEach(value => {
if (typeof value === 'function' && 'mockReset' in value) {
value.mockReset()
}
})
})
describe('DBALClient workflows', () => {
it('creates workflows via adapter', async () => {
mockAdapter.create.mockResolvedValue(workflowRecord)
const client = new DBALClient(baseConfig)
const result = await client.workflows.create(workflowInput)
const payload = mockAdapter.create.mock.calls[0][1]
expect(mockAdapter.create).toHaveBeenCalledWith('Workflow', expect.any(Object))
expect(payload).toMatchObject({
...workflowInput,
tenantId: baseConfig.tenantId,
enabled: true,
version: 1,
})
expect(typeof payload.id).toBe('string')
expect(typeof payload.createdAt).toBe('bigint')
expect(typeof payload.updatedAt).toBe('bigint')
expect(result).toEqual(workflowRecord)
})
it('rejects invalid workflow data before adapter call', async () => {
const client = new DBALClient(baseConfig)
await expect(client.workflows.create({
...workflowInput,
name: '',
})).rejects.toMatchObject({ code: DBALErrorCode.VALIDATION_ERROR })
expect(mockAdapter.create).not.toHaveBeenCalled()
})
it('maps workflow create conflicts to friendly message', async () => {
mockAdapter.create.mockRejectedValue(DBALError.conflict('unique violation'))
const client = new DBALClient(baseConfig)
await expect(client.workflows.create(workflowInput)).rejects.toMatchObject({
code: DBALErrorCode.CONFLICT,
message: "Workflow with name 'daily-sync' already exists",
})
})
it('throws not found when reading missing workflows', async () => {
mockAdapter.findFirst.mockResolvedValue(null)
const client = new DBALClient(baseConfig)
await expect(client.workflows.read(workflowRecord.id)).rejects.toMatchObject({
code: DBALErrorCode.NOT_FOUND,
})
})
it('updates, deletes, and lists workflows', async () => {
const updatedRecord = { ...workflowRecord, name: 'updated-name' }
mockAdapter.findFirst.mockResolvedValue(workflowRecord)
mockAdapter.update.mockResolvedValue(updatedRecord)
mockAdapter.delete.mockResolvedValue(true)
mockAdapter.list.mockResolvedValue({
data: [workflowRecord],
total: 1,
page: 1,
limit: 20,
hasMore: false,
})
const client = new DBALClient(baseConfig)
const updateResult = await client.workflows.update(workflowRecord.id, { name: 'updated-name' })
expect(updateResult).toEqual(updatedRecord)
const deleteResult = await client.workflows.delete(workflowRecord.id)
expect(deleteResult).toBe(true)
const listResult = await client.workflows.list()
expect(listResult.data).toEqual([workflowRecord])
expect(mockAdapter.list).toHaveBeenCalledWith('Workflow', {
filter: { tenantId: baseConfig.tenantId },
})
})
})