fix(gateway): gate FederationModule on tier === 'federated' (#1138) #1140

Merged
shaggy merged 7 commits from fix/1138-conditional-federation into next 2026-08-10 07:09:52 +00:00
2 changed files with 51 additions and 7 deletions
Showing only changes of commit 677aeb0c93 - Show all commits
+40 -4
View File
@@ -12,6 +12,7 @@ interface ComposedModuleGraph {
federationModule: unknown;
bootLogLines: readonly string[];
mosaicConfig: MosaicConfig;
resolvedConfigPath: string;
}
type StorageTier = 'local' | 'standalone' | 'federated';
@@ -205,6 +206,7 @@ async function loadModuleGraphFromDotenv(
delete process.env['MOSAIC_STORAGE_TIER'];
delete process.env['DATABASE_URL'];
delete process.env['VALKEY_URL'];
delete process.env['MOSAIC_GATEWAY_HOME'];
await options.setup?.(fixture);
@@ -235,7 +237,7 @@ async function loadModuleGraphFromDotenv(
expect(process.env['MOSAIC_STORAGE_TIER']).toBe(options.inheritedTier);
}
await import('./env.js');
const envModule = await import('./env.js');
expect(process.env['MOSAIC_STORAGE_TIER']).toBe(
options.expectedProcessTier ?? options.rootTier,
);
@@ -270,6 +272,7 @@ async function loadModuleGraphFromDotenv(
args.map((value: unknown): string => String(value)).join(' '),
),
mosaicConfig: configProvider.useFactory(),
resolvedConfigPath: envModule.resolveGatewayConfigPath(),
};
} finally {
cwdSpy?.mockRestore();
@@ -360,20 +363,53 @@ describe('AppModule federation gating', (): void => {
);
it(
'anchored monorepo-root config wins gateway-local config and registers FederationModule',
'anchored gateway-local config wins monorepo-root config and registers FederationModule',
async (): Promise<void> => {
let gatewayLocalConfigPath = '';
const graph = await loadModuleGraphFromDotenv({
rootTier: 'local',
setup: async (fixture: ModuleGraphFixture): Promise<void> => {
gatewayLocalConfigPath = fixture.gatewayLocalConfigPath;
await writeFixture(
fixture.monorepoRootConfigPath,
fixture.gatewayLocalConfigPath,
configJson('federated'),
fixture.tempRoot,
);
await writeFixture(fixture.gatewayLocalConfigPath, configJson('local'), fixture.tempRoot);
await writeFixture(fixture.monorepoRootConfigPath, configJson('local'), fixture.tempRoot);
},
});
expect(graph.resolvedConfigPath).toBe(gatewayLocalConfigPath);
expect(graph.mosaicConfig.tier).toBe('federated');
expect(graph.imports).toContain(graph.federationModule);
expectBootLogLine(graph.bootLogLines, 'federated', 'mosaic.config.json');
},
MODULE_IMPORT_TIMEOUT_MS,
);
it(
'resolves the daemon-installed GATEWAY_HOME/mosaic.config.json ahead of gateway-local and monorepo-root configs',
async (): Promise<void> => {
let daemonConfigPath = '';
const graph = await loadModuleGraphFromDotenv({
rootEnvMode: 'absent',
setup: async (fixture: ModuleGraphFixture): Promise<void> => {
const externalGatewayHome = join(fixture.tempRoot, 'external-gateway-home');
daemonConfigPath = join(externalGatewayHome, 'mosaic.config.json');
await writeFixture(daemonConfigPath, configJson('federated'), fixture.tempRoot);
await writeFixture(
fixture.gatewayLocalConfigPath,
configJson('standalone'),
fixture.tempRoot,
);
await writeFixture(fixture.monorepoRootConfigPath, configJson('local'), fixture.tempRoot);
process.env['MOSAIC_GATEWAY_HOME'] = externalGatewayHome;
process.env['DATABASE_URL'] = 'postgresql://fixture.invalid/mosaic';
},
});
expect(graph.resolvedConfigPath).toBe(daemonConfigPath);
expect(graph.mosaicConfig.tier).toBe('federated');
expect(graph.imports).toContain(graph.federationModule);
expectBootLogLine(graph.bootLogLines, 'federated', 'mosaic.config.json');
},
+11 -3
View File
@@ -34,15 +34,23 @@ export function resolveGatewayDotenvPaths(
}
export function resolveGatewayConfigPath(anchor: string = here): string {
const monorepoRootConfig = resolve(anchor, '../../..', 'mosaic.config.json');
// GATEWAY_HOME is daemon-created 0700; its env override adds no authority because env can set MOSAIC_STORAGE_TIER.
const gatewayHome = resolve(
process.env['MOSAIC_GATEWAY_HOME'] ?? join(homedir(), '.config', 'mosaic', 'gateway'),
);
const daemonConfig = join(gatewayHome, 'mosaic.config.json');
const gatewayLocalConfig = resolve(anchor, '..', 'mosaic.config.json');
const monorepoRootConfig = resolve(anchor, '../../..', 'mosaic.config.json');
if (existsSync(monorepoRootConfig)) {
return monorepoRootConfig;
if (existsSync(daemonConfig)) {
return daemonConfig;
}
if (existsSync(gatewayLocalConfig)) {
return gatewayLocalConfig;
}
if (existsSync(monorepoRootConfig)) {
return monorepoRootConfig;
}
return monorepoRootConfig;
}