Skip to content

Commit 41d5266

Browse files
committed
test: pin a package's schema-parsed false as a withdrawal, and the env-wide definition as the switch
A package artifact that keeps its public link and never switches `enabled` on carries an explicit `false` once parsed, and that is a withdrawal: an organization-scoped save that opens it is refused. An env-wide save may open a form the package ships closed, and the env-wide list then serves that open body. Claude-Session: https://claude.ai/code/session_018zT8d8NpiQ1ExhuNd5TxY6 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 035a6e0 commit 41d5266

2 files changed

Lines changed: 65 additions & 0 deletions

File tree

‎packages/metadata-core/src/anonymous-form-intake.test.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,18 @@ describe('anonymousFormIntakeWithdrawnIn — an explicit withdrawal of the same
234234
}
235235
});
236236

237+
it('a schema-parsed `false` that keeps the link IS a withdrawal (a package artifact fails closed)', () => {
238+
// A package artifact is served as parsed, and the schema defaults
239+
// `enabled` to false: a shipped sharing that keeps its link and never
240+
// switches `enabled` on carries an explicit `false` once parsed. That is
241+
// a withdrawal. Its raw body, with the switch absent, is not one.
242+
const raw = { allowAnonymous: true, publicLink: '/forms/contact-us' };
243+
const parsed = SharingConfigSchema.parse(raw);
244+
expect(parsed.enabled).toBe(false);
245+
expect(anonymousFormIntakeWithdrawnIn([view(parsed)], openView, candidate)).toBe(true);
246+
expect(anonymousFormIntakeWithdrawnIn([view(raw)], openView, candidate)).toBe(false);
247+
});
248+
237249
it('the same slot with a new slug (case-only included) is the same form: closed', () => {
238250
const withdrawn = [view({ ...OPEN, enabled: false })];
239251
for (const link of ['/forms/contact-us-2', '/forms/Contact-Us']) {

‎packages/metadata-protocol/src/protocol.org-scoped-write-refused.test.ts‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import { afterEach, describe, expect, it } from 'vitest';
7575
// would close a dependency cycle turbo rejects outright.
7676
import { assertEngineDeleteDispatch, assertEngineUpdateDispatch, assertEngineFindOnePredicate } from '@objectstack/metadata-core';
7777
import { DEFAULT_METADATA_TYPE_REGISTRY } from '@objectstack/spec/kernel';
78+
import { SharingConfigSchema } from '@objectstack/spec/ui';
7879
import { ObjectStackProtocolImplementation } from './protocol.js';
7980

8081
interface Row {
@@ -847,4 +848,56 @@ describe('org-scoped anonymous form intake changes the anonymous doors cannot se
847848
});
848849
expect(on.success).toBe(true);
849850
});
851+
852+
// A package's shipped form is part of the env-wide definition, not a layer
853+
// of its own beneath it. A schema-parsed `false` on the artifact (the schema
854+
// defaults `enabled` to false) is an explicit withdrawal, so it fails closed;
855+
// and the env-wide definition is the administrator's switch, so an env-wide
856+
// save may open a form the package ships closed.
857+
describe('single: a package-shipped form', () => {
858+
const LINK = '/forms/walled-intake';
859+
// As the loader serves it: parsed, `enabled` never switched on.
860+
const shipped = {
861+
name: 'task.intake_form', label: 'Intake', object: 'task', viewKind: 'form',
862+
config: { sharing: SharingConfigSchema.parse({ allowAnonymous: true, publicLink: LINK }) },
863+
_packageId: 'showcase',
864+
};
865+
866+
function makePackageProtocol() {
867+
const { engine, rows } = makeStubEngine();
868+
engine.registry.listItems = (type: string) => (type === 'view' ? [shipped] : []);
869+
engine.registry.getArtifactItem = (type: string, name: string) =>
870+
(type === 'view' && name === shipped.name ? shipped : undefined);
871+
const services = new Map<string, unknown>([['tenancy', { defaultOrgId: async () => 'org_a' }]]);
872+
const protocol = new ObjectStackProtocolImplementation(engine, () => services, 'env_prod') as any;
873+
return { protocol, rows };
874+
}
875+
876+
it('the parsed artifact carries an explicit `false` that keeps the link', () => {
877+
expect(shipped.config.sharing).toMatchObject({ enabled: false, allowAnonymous: true, publicLink: LINK });
878+
});
879+
880+
it('a schema-parsed `false` is a withdrawal: an org-scoped save that opens it is refused', async () => {
881+
const { protocol, rows } = makePackageProtocol();
882+
await expect(protocol.saveMetaItem({
883+
type: 'view', name: 'task.intake_form', item: FORM_VIEW(true), organizationId: 'org_a',
884+
})).rejects.toMatchObject({ code: 'NOT_OVERRIDABLE', status: 403, organizationId: 'org_a' });
885+
expect(orgRows(rows).filter((r) => r.org === 'org_a')).toEqual([]);
886+
});
887+
888+
it('the env-wide definition is the switch: an env-wide save opens it, and the env-wide list serves that body', async () => {
889+
const { protocol } = makePackageProtocol();
890+
expect((await protocol.saveMetaItem({ type: 'view', name: 'task.intake_form', item: FORM_VIEW(true) })).success)
891+
.toBe(true);
892+
// The env-wide layer the anonymous doors read beneath an organization.
893+
const envWide: any = await protocol.getMetaItems({ type: 'view' });
894+
const named = (envWide.items as any[]).filter((v) => v?.name === 'task.intake_form');
895+
expect(named).toHaveLength(1);
896+
expect(named[0].config.sharing).toMatchObject({ enabled: true, allowAnonymous: true, publicLink: LINK });
897+
// So an organization overlay that keeps it open is no longer refused.
898+
expect((await protocol.saveMetaItem({
899+
type: 'view', name: 'task.intake_form', item: FORM_VIEW(true), organizationId: 'org_a',
900+
})).success).toBe(true);
901+
});
902+
});
850903
});

0 commit comments

Comments
 (0)