fix(gateway): load dotenv before federation tier gate (#1138)
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import 'reflect-metadata';
|
||||
import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { MODULE_METADATA } from '@nestjs/common/constants.js';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
interface ComposedModuleGraph {
|
||||
imports: readonly unknown[];
|
||||
federationModule: unknown;
|
||||
}
|
||||
|
||||
const MODULE_IMPORT_TIMEOUT_MS = 30_000;
|
||||
|
||||
function restoreEnvironmentVariable(name: string, value: string | undefined): void {
|
||||
if (value === undefined) {
|
||||
delete process.env[name];
|
||||
return;
|
||||
}
|
||||
|
||||
process.env[name] = value;
|
||||
}
|
||||
|
||||
async function loadModuleGraphFromDotenv(
|
||||
tier: 'local' | 'standalone' | 'federated',
|
||||
): Promise<ComposedModuleGraph> {
|
||||
const originalHome = process.env['HOME'];
|
||||
const originalTier = process.env['MOSAIC_STORAGE_TIER'];
|
||||
const originalDatabaseUrl = process.env['DATABASE_URL'];
|
||||
const fixtureRoot = await mkdtemp(join(tmpdir(), 'mosaic-gateway-module-'));
|
||||
const gatewayCwd = join(fixtureRoot, 'apps', 'gateway');
|
||||
|
||||
await mkdir(gatewayCwd, { recursive: true });
|
||||
await writeFile(join(fixtureRoot, '.env'), `MOSAIC_STORAGE_TIER=${tier}\n`, 'utf8');
|
||||
|
||||
process.env['HOME'] = join(fixtureRoot, 'home');
|
||||
delete process.env['MOSAIC_STORAGE_TIER'];
|
||||
delete process.env['DATABASE_URL'];
|
||||
vi.resetModules();
|
||||
const cwdSpy = vi.spyOn(process, 'cwd').mockReturnValue(gatewayCwd);
|
||||
|
||||
try {
|
||||
expect(process.env['MOSAIC_STORAGE_TIER']).toBeUndefined();
|
||||
await import('./env.js');
|
||||
expect(process.env['MOSAIC_STORAGE_TIER']).toBe(tier);
|
||||
|
||||
const { AppModule } = await import('./app.module.js');
|
||||
const { FederationModule } = await import('./federation/federation.module.js');
|
||||
const imports: unknown = Reflect.getMetadata(MODULE_METADATA.IMPORTS, AppModule);
|
||||
|
||||
if (!Array.isArray(imports)) {
|
||||
throw new Error('AppModule imports metadata is not an array');
|
||||
}
|
||||
|
||||
return { imports, federationModule: FederationModule };
|
||||
} finally {
|
||||
cwdSpy.mockRestore();
|
||||
restoreEnvironmentVariable('HOME', originalHome);
|
||||
restoreEnvironmentVariable('MOSAIC_STORAGE_TIER', originalTier);
|
||||
restoreEnvironmentVariable('DATABASE_URL', originalDatabaseUrl);
|
||||
await rm(fixtureRoot, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
describe('AppModule federation gating', (): void => {
|
||||
it('loads dotenv before tracing and AppModule evaluation', async (): Promise<void> => {
|
||||
const mainSource = await readFile(new URL('./main.ts', import.meta.url), 'utf8');
|
||||
const envImportIndex = mainSource.indexOf("import './env.js';");
|
||||
const tracingImportIndex = mainSource.indexOf("import './tracing.js';");
|
||||
const appModuleImportIndex = mainSource.indexOf("import { AppModule } from './app.module.js';");
|
||||
|
||||
expect(envImportIndex).toBeGreaterThan(-1);
|
||||
expect(envImportIndex).toBeLessThan(tracingImportIndex);
|
||||
expect(envImportIndex).toBeLessThan(appModuleImportIndex);
|
||||
});
|
||||
|
||||
it.each(['local', 'standalone'] as const)(
|
||||
'does not register FederationModule for the %s tier',
|
||||
async (tier): Promise<void> => {
|
||||
const graph = await loadModuleGraphFromDotenv(tier);
|
||||
|
||||
expect(graph.imports).not.toContain(graph.federationModule);
|
||||
},
|
||||
MODULE_IMPORT_TIMEOUT_MS,
|
||||
);
|
||||
|
||||
it(
|
||||
'registers FederationModule when federated tier is supplied only by a dotenv file',
|
||||
async (): Promise<void> => {
|
||||
const graph = await loadModuleGraphFromDotenv('federated');
|
||||
|
||||
expect(graph.imports).toContain(graph.federationModule);
|
||||
},
|
||||
MODULE_IMPORT_TIMEOUT_MS,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user