/**
 * Regression tests for the `/api/cms/collections` discovery endpoint
 * shipped in PR F (Phase 7 — MCP-parity stylistic cleanup).
 *
 * Purpose: enumerate the configured collections without relying on the
 * OpenAPI generator, which has historically drifted out of sync with
 * the admin-visible surface. The endpoint is unauthenticated by design
 * — the response contains schema-shape metadata that is already public
 * via `/openapi.json` — but we test that contract here so a future
 * change can't silently widen it.
 */
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { handleActuateAPI } from '../../api/index.js';
import { setActuateConfig } from '../../config/runtime.js';
const SECRET = 'test-secret-collections-discovery-pr-f-12345';
describe('GET /api/cms/collections (PR F discovery endpoint)', () => {
    // Snapshot every env var any test in this file touches so we can
    // restore them in `afterEach`. Mirrors the pattern in health.test.ts —
    // PR #155 review (Bugbot) caught that the prior implementation set
    // CMS_SECRET in beforeEach but never restored it, leaking state into
    // any other test that runs after this file.
    const original = {
        CMS_SECRET: process.env.CMS_SECRET,
        CMS_SESSION_SECRET: process.env.CMS_SESSION_SECRET,
    };
    beforeEach(() => {
        process.env.CMS_SECRET = SECRET;
        delete process.env.CMS_SESSION_SECRET;
    });
    afterEach(() => {
        for (const [k, v] of Object.entries(original)) {
            if (v === undefined)
                delete process.env[k];
            else
                process.env[k] = v;
        }
        delete globalThis.__actuateConfig;
    });
    function makeHandler() {
        setActuateConfig({
            secret: SECRET,
            collections: {
                posts: {
                    slug: 'posts',
                    labels: { singular: 'Post', plural: 'Posts' },
                    type: 'post',
                    urlPrefix: 'blog',
                    fields: {
                        title: { type: 'text', required: true },
                        body: { type: 'richText' },
                        author: { type: 'relationship', relationTo: 'users' },
                    },
                },
                pages: {
                    slug: 'pages',
                    labels: { singular: 'Page', plural: 'Pages' },
                    type: 'page',
                    fields: {
                        title: { type: 'text', required: true },
                        slug: { type: 'slug', required: true },
                    },
                },
                hidden_internal: {
                    slug: 'hidden_internal',
                    labels: { singular: 'Internal', plural: 'Internal' },
                    fields: { name: { type: 'text' } },
                    admin: { hidden: true },
                },
            },
        });
        return handleActuateAPI({ prismaClient: {} });
    }
    it('returns one entry per configured collection with field summaries', async () => {
        const handler = makeHandler();
        const response = await handler(new Request('https://example.com/api/cms/collections', { method: 'GET' }));
        expect(response.status).toBe(200);
        const payload = (await response.json());
        expect(payload.data).toHaveLength(3);
        const bySlug = new Map(payload.data.map((c) => [c.slug, c]));
        const posts = bySlug.get('posts');
        expect(posts.labels).toEqual({ singular: 'Post', plural: 'Posts' });
        expect(posts.type).toBe('post');
        expect(posts.fieldCount).toBe(3);
        expect(posts.fields).toEqual(expect.arrayContaining([
            { name: 'title', type: 'text', required: true },
            { name: 'body', type: 'richText', required: false },
            { name: 'author', type: 'relationship', required: false },
        ]));
        const pages = bySlug.get('pages');
        expect(pages.type).toBe('page');
        expect(pages.fieldCount).toBe(2);
        expect(pages.fields.find((f) => f.name === 'title')?.required).toBe(true);
    });
    it('surfaces admin.hidden as `hidden: true` so agents can choose to skip them', async () => {
        // Internal collections should still be discoverable — the agent
        // might legitimately want to read from them — but the flag lets
        // the agent down-rank them in its planning.
        const handler = makeHandler();
        const response = await handler(new Request('https://example.com/api/cms/collections', { method: 'GET' }));
        const payload = (await response.json());
        const hiddenEntry = payload.data.find((c) => c.slug === 'hidden_internal');
        expect(hiddenEntry?.hidden).toBe(true);
        const postsEntry = payload.data.find((c) => c.slug === 'posts');
        expect(postsEntry?.hidden).toBe(false);
    });
    it('returns 500 when no CMS config is registered (deploy misconfig)', async () => {
        // Defensive: route must error loudly when getActuateConfig() is
        // missing instead of returning `{ data: [] }`, which would lie to
        // the MCP fallback chain about the server's actual state.
        const handler = handleActuateAPI({ prismaClient: {} });
        const response = await handler(new Request('https://example.com/api/cms/collections', { method: 'GET' }));
        expect(response.status).toBe(500);
    });
    it('surfaces admin.description / icon / color / group for the Posts admin area', async () => {
        // The Posts dashboard area renders per-collection cards that need
        // these visual hints (icon, accent color, description) plus the
        // grouping marker. Locking the contract here keeps the admin
        // client and server in sync — if a field is renamed server-side,
        // the test catches it before /posts/types renders empty cards.
        setActuateConfig({
            secret: SECRET,
            collections: {
                blog: {
                    slug: 'blog',
                    labels: { singular: 'Blog Post', plural: 'Blog' },
                    type: 'post',
                    urlPrefix: 'blog',
                    fields: { title: { type: 'text', required: true } },
                    admin: {
                        group: 'Posts',
                        description: 'Blog posts and articles.',
                        icon: 'file-text',
                        color: 'purple',
                    },
                },
                pages: {
                    slug: 'pages',
                    labels: { singular: 'Page', plural: 'Pages' },
                    type: 'page',
                    fields: { title: { type: 'text', required: true } },
                },
            },
        });
        const handler = handleActuateAPI({ prismaClient: {} });
        const response = await handler(new Request('https://example.com/api/cms/collections', { method: 'GET' }));
        const payload = (await response.json());
        const blog = payload.data.find((c) => c.slug === 'blog');
        expect(blog.description).toBe('Blog posts and articles.');
        expect(blog.icon).toBe('file-text');
        expect(blog.color).toBe('purple');
        expect(blog.group).toBe('Posts');
        expect(blog.urlPrefix).toBe('blog');
        // Collections without admin metadata should emit `null` (not
        // `undefined`) so the wire shape stays JSON-stable.
        const pages = payload.data.find((c) => c.slug === 'pages');
        expect(pages.description).toBeNull();
        expect(pages.icon).toBeNull();
        expect(pages.color).toBeNull();
        expect(pages.group).toBeNull();
    });
    it('does not leak deeper schema (validators, hooks, access functions)', async () => {
        // The response is unauthenticated — field-level validators or
        // access functions could include source pointers / closure state
        // that mustn't ship in the public surface. Keep the contract
        // tight: only `name`, `type`, `required` per field.
        const handler = makeHandler();
        const response = await handler(new Request('https://example.com/api/cms/collections', { method: 'GET' }));
        const payload = (await response.json());
        for (const collection of payload.data) {
            for (const field of collection.fields) {
                expect(Object.keys(field).sort()).toEqual(['name', 'required', 'type']);
            }
        }
    });
});
//# sourceMappingURL=collections-discovery.test.js.map