Files
liqa/server/test/app.harness.ts
ars9 6a91ce30e3 feat(files): add file storage subsystem with scenario and run artifact support
- add FileEntity, ScenarioFileEntity, ScenarioRunFileEntity with UUID-sharded disk tree
- FileStorageService: write/sha256/expiry, paginated listing, dynamic cleanup cron via SchedulerRegistry
- expose getScenarioFiles() and downloadFile() on ScriptContext for use in exec code
- wire scenarioId, runId, fileService into ExecContextBuilder and scenario scheduler
- add file upload and listing endpoints to ScenarioController
- add FILES_DIR, FILE_RUN_EXPIRATION_DAYS, FILE_CLEANUP_CRON to AppConfig
2026-04-21 13:01:09 +03:00

100 lines
3.9 KiB
TypeScript

import { INestApplication, ValidationPipe } from "@nestjs/common";
import { Test, TestingModule } from "@nestjs/testing";
import { TypeOrmModule } from "@nestjs/typeorm";
import { ConfigModule } from "@nestjs/config";
import { ScheduleModule } from "@nestjs/schedule";
jest.mock("@nestjs/common", () => {
const actual = jest.requireActual("@nestjs/common");
// eslint-disable-next-line @typescript-eslint/no-require-imports
const log = require("debug")("test");
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { Logger } = require("@nestjs/common/services/logger.service");
Logger.prototype.error = function (
message: unknown,
stack?: string,
context?: string,
) {
const ctx = context ?? this.context ?? "App";
log(`[${ctx}]`, "error", message, ...(stack ? [stack] : []));
};
for (const level of ["log", "warn", "debug", "verbose", "fatal"] as const) {
Logger.prototype[level] = function (message: unknown, context?: string) {
const ctx = context ?? this.context ?? "App";
log(`[${ctx}]`, level, message);
};
}
return actual;
});
import { BrowserModule } from "../src/browser/browser.module";
import { SessionModule } from "../src/session/session.module";
import { EnvironmentModule } from "../src/environment/environment.module";
import { ScenarioModule } from "../src/scenario/scenario.module";
import { McpModule } from "../src/mcp/mcp.module";
import { SessionEntity } from "../src/session/session.entity";
import { EnvironmentEntity } from "../src/environment/environment.entity";
import { ScenarioEntity } from "../src/scenario/scenario.entity";
import { ScenarioStepEntity } from "../src/scenario/scenario-step.entity";
import { ScenarioRunEntity } from "../src/scenario/scenario-run.entity";
import { ScenarioRunStepEntity } from "../src/scenario/scenario-run-step.entity";
import { ScenarioRunLogEntity } from "../src/scenario/scenario-run-log.entity";
import { ScenarioCredentialEntity } from "../src/scenario/scenario-credential.entity";
import { CredentialEntity } from "../src/credential/credential.entity";
import { SnippetEntity } from "../src/snippet/snippet.entity";
import { HealthController } from "../src/health/health.controller";
import { HttpExceptionFilter } from "../src/filters/http-exception.filter";
import { LoggingInterceptor } from "../src/interceptors/logging.interceptor";
import { validateAppConfig } from "../src/config/app.config";
import { FileEntity } from "../src/file/file.entity";
import { ScenarioFileEntity } from "../src/file/scenario-file.entity";
import { ScenarioRunFileEntity } from "../src/file/scenario-run-file.entity";
export async function buildTestApp(): Promise<INestApplication> {
const module: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
isGlobal: true,
ignoreEnvFile: true,
validate: validateAppConfig,
}),
TypeOrmModule.forRoot({
type: "better-sqlite3",
database: ":memory:",
entities: [
SessionEntity,
EnvironmentEntity,
ScenarioEntity,
ScenarioStepEntity,
ScenarioRunEntity,
ScenarioRunStepEntity,
ScenarioRunLogEntity,
ScenarioCredentialEntity,
CredentialEntity,
SnippetEntity,
FileEntity,
ScenarioFileEntity,
ScenarioRunFileEntity,
],
synchronize: true,
}),
ScheduleModule.forRoot(),
BrowserModule,
SessionModule,
EnvironmentModule,
ScenarioModule,
McpModule,
],
controllers: [HealthController],
}).compile();
const app = module.createNestApplication();
app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));
app.useGlobalFilters(new HttpExceptionFilter());
app.useGlobalInterceptors(new LoggingInterceptor());
await app.init();
return app;
}