Skip to content

Commit 8b2647e

Browse files
committed
test(dogfood): the federation pin asserts the harness's composed MetadataPlugin, not the fallback
The precondition was written to red the day bootStack composes a metadata plugin; it now pins that occupant (registered, a MetadataManager, with the endpoint matcher). The header drops the objectstack start framing and names the seam pin that holds the init-ordering fix. The two federation cases are unchanged. Claude-Session: https://claude.ai/code/session_01VoSxBQujKLZKPwK2u5ehQ6 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3867e0a commit 8b2647e

1 file changed

Lines changed: 29 additions & 22 deletions

File tree

‎packages/qa/dogfood/test/external-validate-start-ordering.dogfood.test.ts‎

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
22
//
3-
// [#21876] On a composition with no metadata plugin — the `objectstack start`
4-
// shape — the federation service compares every federated object.
3+
// [#21876] The federation service compares every federated object, on the
4+
// composition the verify harness boots.
55
//
66
// ## What was broken
77
//
88
// `ExternalDatasourceServicePlugin.init()` read the `metadata` service once and
9-
// kept the answer. `objectstack start` composes no metadata plugin: its
10-
// `metadata` service is the kernel's in-memory fallback, registered after every
11-
// plugin's `init()`, just before the start phase. So the federation service
12-
// kept "no metadata service" for the life of the process. Measured on the
13-
// showcase under `objectstack start`: `POST /datasources/:name/external/validate`
14-
// answered `ok: true` with no rows, and the boot gate logged "all federated
15-
// objects match their remote schema" with `objects: 0` — although two
16-
// federated objects exist, and one of them could have drifted. Under
17-
// `objectstack dev` (which composes the metadata plugin before this one) both
18-
// objects were compared. The service now resolves `metadata` when it is used.
9+
// kept the answer. On a composition with no metadata plugin, that service is
10+
// the kernel's in-memory fallback, registered after every plugin's `init()`,
11+
// just before the start phase. So the federation service kept "no metadata
12+
// service" for the life of the process. Measured on the showcase at the time:
13+
// `POST /datasources/:name/external/validate` answered `ok: true` with no rows,
14+
// and the boot gate logged "all federated objects match their remote schema"
15+
// with `objects: 0` — although two federated objects exist, and one of them
16+
// could have drifted. The service now resolves `metadata` when it is used.
17+
// That ordering fix is pinned at its seam, in
18+
// `packages/services/service-datasource/src/__tests__/external-validate-reads-live-registry.test.ts`.
1919
//
2020
// ## Why this composition
2121
//
22-
// The verify harness composes no metadata plugin either, so its `metadata`
23-
// service is the same kernel fallback, registered at the same moment, as on
24-
// `objectstack start`. The first case asserts that, so a harness that one day
25-
// composes a metadata plugin cannot turn this file into a silent pass. The
26-
// harness does not mount the federation service, so this file mounts it, as
22+
// The verify harness composes `@objectstack/metadata`'s `MetadataPlugin`, the
23+
// artifact-less one `objectstack serve` and `start` give a host configuration
24+
// such as the showcase (#23088). The first case asserts that occupant: the
25+
// plugin is registered, and the `metadata` service is its manager, with the
26+
// endpoint matcher. So a change to what the harness composes cannot pass
27+
// through this file unnoticed. The harness does not mount the federation
28+
// service, so this file mounts it, as
2729
// `objectstack dev` and `start` do. It does not mount the boot gate either
2830
// (`createExternalValidationPlugin`, `@objectstack/runtime`): the gate's input
2931
// is the federation service's whole-farm sweep, `validateAll()`, which the
@@ -41,6 +43,7 @@
4143
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
4244
import showcaseStack, { onEnable } from '@objectstack/example-showcase';
4345
import { ExternalDatasourceServicePlugin, resolveSqliteDriver } from '@objectstack/service-datasource';
46+
import { MetadataManager } from '@objectstack/metadata';
4447
import { type VerifyStack } from '@objectstack/verify';
4548
import { bootShowcase } from './showcase-boot.js';
4649
import { mkdtempSync, rmSync } from 'node:fs';
@@ -82,7 +85,7 @@ async function driftRemoteCustomers(): Promise<void> {
8285
}
8386
}
8487

85-
describe('federation on a composition with no metadata plugin (the objectstack start shape)', () => {
88+
describe('federation on the harness composition, which holds the metadata plugin', () => {
8689
let stack: VerifyStack;
8790
let token: string;
8891
let prevCwd: string;
@@ -107,10 +110,14 @@ describe('federation on a composition with no metadata plugin (the objectstack s
107110
if (dir) rmSync(dir, { recursive: true, force: true });
108111
});
109112

110-
it('precondition: the metadata service is the kernel\'s in-memory fallback, as on objectstack start', () => {
111-
const metadata = stack.kernel.getService<{ __serviceInfo?: { status?: string; message?: string } }>('metadata');
112-
expect(metadata.__serviceInfo?.status).toBe('degraded');
113-
expect(metadata.__serviceInfo?.message).toMatch(/^In-memory metadata registry/);
113+
it('precondition: the metadata service is the composed MetadataPlugin\'s manager, with its endpoint matcher', () => {
114+
// The plugin is registered under its own name, and the `metadata` slot is
115+
// its `MetadataManager`, not the kernel's in-memory fallback (a plain
116+
// object with no matcher).
117+
expect(stack.kernel.hasPlugin('com.objectstack.metadata')).toBe(true);
118+
const metadata = stack.kernel.getService<{ matchEndpoint?: unknown }>('metadata');
119+
expect(metadata).toBeInstanceOf(MetadataManager);
120+
expect(typeof metadata.matchEndpoint).toBe('function');
114121
});
115122

116123
it('validate compares each federated object, and reports the drifted column', async () => {

0 commit comments

Comments
 (0)