// Unit test with `@voltro/testing` — no database, no running server. Importing // `../database/schema` registers the `notes` / `actors` / `tenants` tables // globally, so `makeTestContext` auto-wires them into the in-memory store. // `ctx.store` is the SAME mixin-wrapped store the handler gets in production, // so tenant auto-scoping + audit auto-fill behave exactly as at runtime. // Run with `voltro test` (vitest). import { describe, it, expect } from 'vitest' import { makeTestContext, mockStore } from '@voltro/testing' import { database } from '../database/schema' // registers notes / actors / tenants import { TenantMismatch } from '@voltro/plugin-multitenancy/guard' import createNote from '../mutations/notes.create.mutation.server' describe('notes.create', () => { it('inserts a note stamped to the caller tenant', async () => { const ctx = makeTestContext({ subject: { type: 'user', id: 'user_1', tenantId: 'acme' }, store: mockStore({ notes: [] }), }) const row = await createNote({ tenantId: 'acme', title: 'Hello', body: 'World' }, ctx) expect(row['title']).toBe('Hello') expect(row['tenantId']).toBe('acme') // assertOwnTenant let it through + stamped expect(row['done']).toBe(false) expect(String(row['id'])).toMatch(/^note_/) // TypeID auto-injected from id() }) it('rejects a cross-tenant spoof — input.tenantId must match the subject', async () => { const ctx = makeTestContext({ subject: { type: 'user', id: 'user_1', tenantId: 'acme' }, store: mockStore({ notes: [] }), }) await expect( createNote({ tenantId: 'other-tenant', title: 'spoof', body: '' }, ctx), ).rejects.toBeInstanceOf(TenantMismatch) }) it('isolates tenants — t2 never sees t1 rows (REAL tenant scoping)', async () => { const ctx = makeTestContext({ subject: { type: 'user', id: 'u1', tenantId: 't1' }, store: mockStore({ notes: [] }), }) await createNote({ tenantId: 't1', title: 'secret', body: '' }, ctx) // Re-scope the SAME store to a different tenant — the real WHERE filter // (eq tenantId) hides t1's row from t2. const seenByT2 = await ctx.withTenant('t2', (c) => c.store.query(database.notes.descriptor)) expect(seenByT2).toHaveLength(0) }) })