// Unit tests with `@voltro/testing` — no database, no running server, no // billing backend. The create executors run in Effect mode and `yield*` the // billing service (`requireEntitlement`), which lives in the per-request plugin // layer the unit harness does NOT wire — so we do not fake it. Instead we PIN // each descriptor's wire contract (the shape the client codegen + the paywall // branch depend on) and prove the `tenant()` scoping that keeps one SaaS // tenant's rows invisible to another — the headline isolation guarantee. // Run with `voltro test` (vitest). import { describe, it, expect } from 'vitest' import { Schema } from 'effect' import { makeTestContext, mockStore } from '@voltro/testing' import { database } from '../database/schema' // registers projects / invites / actors / tenants import { createProject } from '../mutations/projects.create.mutation' import { createInvite } from '../mutations/invites.create.mutation' describe('projects.create descriptor', () => { it('decodes a valid input and rejects a blank name', () => { const decode = Schema.decodeUnknownSync(createProject.input) expect(decode({ name: 'Acme App' })).toEqual({ name: 'Acme App' }) // `name: Schema.NonEmptyString` — an empty string fails the wire decode. expect(() => decode({ name: '' })).toThrow() }) it('encodes the output shape the client expects', () => { const encode = Schema.encodeUnknownSync(createProject.output) const wire = encode({ id: 'proj_1', name: 'Acme App', tenantId: 'acme' }) expect(wire).toEqual({ id: 'proj_1', name: 'Acme App', tenantId: 'acme' }) }) }) describe('invites.create descriptor', () => { it('decodes an email and rejects a blank one', () => { const decode = Schema.decodeUnknownSync(createInvite.input) expect(decode({ email: 'ada@example.com' })).toEqual({ email: 'ada@example.com' }) expect(() => decode({ email: '' })).toThrow() }) it('encodes the invite output shape', () => { const encode = Schema.encodeUnknownSync(createInvite.output) const wire = encode({ id: 'inv_1', email: 'ada@example.com', status: 'pending', tenantId: 'acme' }) expect(wire).toEqual({ id: 'inv_1', email: 'ada@example.com', status: 'pending', tenantId: 'acme' }) }) }) describe('tenant isolation (REAL tenant scoping)', () => { it('t2 never sees t1 projects', async () => { const ctx = makeTestContext({ subject: { type: 'user', id: 'u1', tenantId: 't1' }, store: mockStore({ projects: [], invites: [] }), }) // Raw insert through the mixin store — tenant() auto-stamps tenantId=t1. const row = await ctx.store.insert('projects', { name: 'secret' }) expect(row['tenantId']).toBe('t1') const seenByT2 = await ctx.withTenant('t2', (c) => c.store.query(database.projects.descriptor)) expect(seenByT2).toHaveLength(0) // …and t1 still sees its own row. const seenByT1 = await ctx.store.query(database.projects.descriptor) expect(seenByT1).toHaveLength(1) }) })