/** * End-to-end: a local-org member sees 'org'-scoped resources; a non-member * does not. Validates the whole chain — local org membership → resolveOrgIds * → session.orgIds → the provider-agnostic buildVisibilityWhere. * * See docs/superpowers/plans/2026-06-09-local-orgs.md. */ import { afterEach, beforeEach, describe, it, expect } from 'vitest'; import { mkdtempSync, rmSync } from 'fs'; import { join } from 'path'; import { tmpdir } from 'os'; import { Repository } from '../db/repository.js'; import { runMigrations } from '../db/migrate.js'; import { resolveOrgIds } from './auth.js'; function viewer(id: string, orgIds: string[]): Express.User { return { id, email: `${id}@x.com`, name: id, avatarUrl: null, role: 'user', status: 'active', orgIds, defaultVisibility: 'private', defaultVisibilityOrgId: null, }; } describe('local orgs — org visibility E2E', () => { let tempDir = ''; let repo: Repository; beforeEach(() => { tempDir = mkdtempSync(join(tmpdir(), 'maestro-orgvis-')); repo = new Repository(join(tempDir, 'orchestrator.db')); runMigrations(repo.getDb()); }); afterEach(() => { repo.close(); if (tempDir) { rmSync(tempDir, { recursive: true, force: true }); tempDir = ''; } }); it('resolveOrgIds includes the local orgs a user belongs to', () => { const alice = repo.createUser({ email: 'a@x.com', name: 'A', role: 'user', status: 'active' }).id; const org = repo.createLocalOrg('Team', alice); repo.addOrgMember(org.id, alice); expect(resolveOrgIds(repo, alice)).toContain(org.id); }); it('an org member sees an org-scoped task; a non-member does not', async () => { const carol = repo.createUser({ email: 'carol@x.com', name: 'Carol', role: 'user', status: 'active' }).id; const alice = repo.createUser({ email: 'alice@x.com', name: 'Alice', role: 'user', status: 'active' }).id; const bob = repo.createUser({ email: 'bob@x.com', name: 'Bob', role: 'user', status: 'active' }).id; const org = repo.createLocalOrg('Team', carol); repo.addOrgMember(org.id, carol); repo.addOrgMember(org.id, alice); // alice is a member, bob is not await repo.createLocalTask({ title: 'org task', body: 'b', ownerId: carol, visibility: 'org', visibilityScopeOrgId: org.id, }); const aliceTasks = await repo.listLocalTasks({ viewer: viewer(alice, resolveOrgIds(repo, alice)) }); const bobTasks = await repo.listLocalTasks({ viewer: viewer(bob, resolveOrgIds(repo, bob)) }); expect(aliceTasks.some(t => t.title === 'org task')).toBe(true); // member sees it expect(bobTasks.some(t => t.title === 'org task')).toBe(false); // non-member does not }); it('the org name resolves on display after the COALESCE extension', async () => { const carol = repo.createUser({ email: 'c@x.com', name: 'C', role: 'user', status: 'active' }).id; const org = repo.createLocalOrg('Engineering', carol); repo.addOrgMember(org.id, carol); const t = await repo.createLocalTask({ title: 'x', body: 'b', ownerId: carol, visibility: 'org', visibilityScopeOrgId: org.id, }); const got = await repo.getLocalTask(t.id, { viewer: viewer(carol, resolveOrgIds(repo, carol)) }); expect(got?.visibilityScopeOrgName).toBe('Engineering'); }); });