diff --git a/.eslintrc.js b/.eslintrc.js index 581d7dea23a..a70d46f9a2b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -65,7 +65,7 @@ module.exports = { }, overrides: [ { - files: ['apps/**/*.ts'], + files: ['apps/server/src/**/*.ts'], env: { node: true, es6: true, @@ -92,7 +92,7 @@ module.exports = { 'class-methods-use-this': 'off', 'no-param-reassign': 'off', 'no-underscore-dangle': 'off', - 'filename-rules/match': [1, 'kebabcase'], + 'filename-rules/match': [1, /^([a-z0-9]+-)*[a-z]+(?:\..*)?$/], 'require-await': 'warn', '@typescript-eslint/unbound-method': 'error', '@typescript-eslint/no-non-null-assertion': 'warn', diff --git a/apps/server/src/apps/server-console.app.ts b/apps/server/src/apps/server-console.app.ts index e82cfdc5ace..93fbac6489c 100644 --- a/apps/server/src/apps/server-console.app.ts +++ b/apps/server/src/apps/server-console.app.ts @@ -1,14 +1,14 @@ /* istanbul ignore file */ /* eslint-disable promise/always-return */ +import { ManagementConsoleModule } from '@modules/management/management-console.app.module'; import { BootstrapConsole } from 'nestjs-console'; -import { ServerConsoleModule } from '../modules/server-console/server-console.app.module'; /** * The console is starting the application wrapped into commander. * This allows adding console commands to execute provider methods. */ const bootstrap = new BootstrapConsole({ - module: ServerConsoleModule, + module: ManagementConsoleModule, useDecorators: true, }); void bootstrap.init().then(async (app) => { diff --git a/apps/server/src/apps/server.app.ts b/apps/server/src/apps/server.app.ts index 61aa39716a5..a52cec4b736 100644 --- a/apps/server/src/apps/server.app.ts +++ b/apps/server/src/apps/server.app.ts @@ -116,7 +116,7 @@ async function bootstrap(): Promise { // logger middleware for deprecated paths // TODO remove when all calls to the server are migrated - const logDeprecatedPaths = (req: express.Request, res: express.Response, next: express.NextFunction) => { + const logDeprecatedPaths = (req: express.Request, res: express.Response, next: express.NextFunction): void => { legacyLogger.error(req.path, 'DEPRECATED-PATH'); next(); }; diff --git a/apps/server/src/infra/database/index.ts b/apps/server/src/infra/database/index.ts deleted file mode 100644 index 4dfac277f40..00000000000 --- a/apps/server/src/infra/database/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './management'; diff --git a/apps/server/src/infra/database/management/database-management.module.spec.ts b/apps/server/src/infra/database/management/database-management.module.spec.ts deleted file mode 100644 index a8cae6a7dd8..00000000000 --- a/apps/server/src/infra/database/management/database-management.module.spec.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { MongoMemoryDatabaseModule } from '@testing/database'; -import { DatabaseManagementModule } from './database-management.module'; -import { DatabaseManagementService } from './database-management.service'; - -describe('DatabaseManagementModule', () => { - let module: TestingModule; - let service: DatabaseManagementService; - - beforeAll(async () => { - module = await Test.createTestingModule({ - imports: [DatabaseManagementModule, MongoMemoryDatabaseModule.forRoot()], - }).compile(); - service = module.get(DatabaseManagementService); - }); - - afterAll(async () => { - await module.close(); - }); - - it('should have defined service', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/apps/server/src/infra/database/management/database-management.module.ts b/apps/server/src/infra/database/management/database-management.module.ts deleted file mode 100644 index e15a699c828..00000000000 --- a/apps/server/src/infra/database/management/database-management.module.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Module } from '@nestjs/common'; -import { DatabaseManagementService } from './database-management.service'; - -@Module({ - providers: [DatabaseManagementService], - exports: [DatabaseManagementService], -}) -export class DatabaseManagementModule {} diff --git a/apps/server/src/infra/database/management/index.ts b/apps/server/src/infra/database/management/index.ts deleted file mode 100644 index 8f89890cd94..00000000000 --- a/apps/server/src/infra/database/management/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './database-management.service'; -export * from './database-management.module'; diff --git a/apps/server/src/infra/identity-management/identity-management.module.spec.ts b/apps/server/src/infra/identity-management/identity-management.module.spec.ts index 76e493dbd62..26c63f908d2 100644 --- a/apps/server/src/infra/identity-management/identity-management.module.spec.ts +++ b/apps/server/src/infra/identity-management/identity-management.module.spec.ts @@ -1,6 +1,5 @@ import { ConfigModule } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; -import { MongoMemoryDatabaseModule } from '@testing/database'; import { IdentityManagementModule } from './identity-management.module'; import { IdentityManagementService } from './identity-management.service'; @@ -12,7 +11,6 @@ describe('IdentityManagementModule', () => { module = await Test.createTestingModule({ imports: [ IdentityManagementModule, - MongoMemoryDatabaseModule.forRoot(), ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true, isGlobal: true }), ], }).compile(); diff --git a/apps/server/src/infra/identity-management/keycloak-configuration/keycloak-configuration.module.spec.ts b/apps/server/src/infra/identity-management/keycloak-configuration/keycloak-configuration.module.spec.ts index e82e7647322..eadb49dce2f 100644 --- a/apps/server/src/infra/identity-management/keycloak-configuration/keycloak-configuration.module.spec.ts +++ b/apps/server/src/infra/identity-management/keycloak-configuration/keycloak-configuration.module.spec.ts @@ -1,3 +1,4 @@ +import { SystemEntity } from '@modules/system/entity'; import { ConfigModule } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; import { MongoMemoryDatabaseModule } from '@testing/database'; @@ -12,7 +13,7 @@ describe('KeycloakManagementModule', () => { module = await Test.createTestingModule({ imports: [ KeycloakConfigurationModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [SystemEntity] }), ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true, isGlobal: true }), ], }).compile(); diff --git a/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.integration.spec.ts b/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.integration.spec.ts index 92d6f200fa3..97645067634 100644 --- a/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.integration.spec.ts +++ b/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.integration.spec.ts @@ -3,6 +3,7 @@ import KeycloakAdminClient from '@keycloak/keycloak-admin-client-cjs/keycloak-ad import AuthenticationExecutionExportRepresentation from '@keycloak/keycloak-admin-client/lib/defs/authenticationExecutionExportRepresentation'; import AuthenticationFlowRepresentation from '@keycloak/keycloak-admin-client/lib/defs/authenticationFlowRepresentation'; import { EntityManager } from '@mikro-orm/mongodb'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { systemEntityFactory } from '@modules/system/testing'; import { ConfigModule } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; @@ -29,7 +30,7 @@ describe('KeycloakConfigurationService Integration', () => { imports: [ KeycloakConfigurationModule, LoggerModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity] }), ConfigModule.forRoot({ isGlobal: true, validationOptions: { infer: true }, diff --git a/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.integration.spec.ts b/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.integration.spec.ts index d894e2ea3c3..c879b86534d 100644 --- a/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.integration.spec.ts +++ b/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.integration.spec.ts @@ -53,7 +53,7 @@ describe('KeycloakConfigurationService Integration', () => { imports: [ KeycloakConfigurationModule, LoggerModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity] }), ConfigModule.forRoot({ isGlobal: true, ignoreEnvFile: true, diff --git a/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-seed.service.integration.spec.ts b/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-seed.service.integration.spec.ts index 80048bb7278..a3700e3ade7 100644 --- a/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-seed.service.integration.spec.ts +++ b/apps/server/src/infra/identity-management/keycloak-configuration/service/keycloak-seed.service.integration.spec.ts @@ -1,6 +1,7 @@ import { LoggerModule } from '@core/logger'; import { faker } from '@faker-js/faker'; import KeycloakAdminClient from '@keycloak/keycloak-admin-client'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { ConfigModule } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; import { MongoMemoryDatabaseModule } from '@testing/database'; @@ -35,7 +36,7 @@ describe('KeycloakSeedService Integration', () => { imports: [ KeycloakConfigurationModule, LoggerModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity] }), ConfigModule.forRoot({ isGlobal: true, ignoreEnvFile: true, diff --git a/apps/server/src/infra/identity-management/keycloak/keycloak.module.spec.ts b/apps/server/src/infra/identity-management/keycloak/keycloak.module.spec.ts index e65fc72c423..2dc51230625 100644 --- a/apps/server/src/infra/identity-management/keycloak/keycloak.module.spec.ts +++ b/apps/server/src/infra/identity-management/keycloak/keycloak.module.spec.ts @@ -1,6 +1,5 @@ import { ConfigModule } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; -import { MongoMemoryDatabaseModule } from '@testing/database'; import { KeycloakModule } from './keycloak.module'; import { KeycloakIdentityManagementOauthService } from './service/keycloak-identity-management-oauth.service'; import { KeycloakIdentityManagementService } from './service/keycloak-identity-management.service'; @@ -10,11 +9,7 @@ describe('KeycloakModule', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [ - KeycloakModule, - MongoMemoryDatabaseModule.forRoot(), - ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true, isGlobal: true }), - ], + imports: [KeycloakModule, ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true, isGlobal: true })], }).compile(); }); diff --git a/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management-oauth.service.integration.spec.ts b/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management-oauth.service.integration.spec.ts index ee525326573..8540ef82ce6 100644 --- a/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management-oauth.service.integration.spec.ts +++ b/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management-oauth.service.integration.spec.ts @@ -1,5 +1,6 @@ import { LoggerModule } from '@core/logger'; import { KeycloakModule } from '@infra/identity-management/keycloak/keycloak.module'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { ConfigModule } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; import { MongoMemoryDatabaseModule } from '@testing/database'; @@ -26,7 +27,7 @@ describe('KeycloakIdentityManagementOauthService Integration', () => { KeycloakConfigurationModule, KeycloakAdministrationModule, LoggerModule, - MongoMemoryDatabaseModule.forRoot({ allowGlobalContext: true }), + MongoMemoryDatabaseModule.forRoot({ allowGlobalContext: true, entities: [AccountEntity] }), ConfigModule.forRoot({ isGlobal: true }), ], }).compile(); diff --git a/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management.service.ts b/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management.service.ts index e6655401155..b01dbae1827 100644 --- a/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management.service.ts +++ b/apps/server/src/infra/identity-management/keycloak/service/keycloak-identity-management.service.ts @@ -8,11 +8,11 @@ import { KeycloakAdministrationService } from '../../keycloak-administration/ser @Injectable() export class KeycloakIdentityManagementService extends IdentityManagementService { - public constructor(private readonly kcAdminClient: KeycloakAdministrationService) { + constructor(private readonly kcAdminClient: KeycloakAdministrationService) { super(); } - async createAccount(account: IdmAccount, password?: string): Promise { + public async createAccount(account: IdmAccount, password?: string): Promise { const kc = await this.kcAdminClient.callKcAdminClient(); const id = await kc.users.create({ username: account.username, @@ -44,7 +44,7 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return id.id; } - async updateAccount(id: string, account: IdmAccountUpdate): Promise { + public async updateAccount(id: string, account: IdmAccountUpdate): Promise { await ( await this.kcAdminClient.callKcAdminClient() ).users.update( @@ -60,7 +60,7 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return id; } - async updateAccountPassword(id: string, password: string): Promise { + public async updateAccountPassword(id: string, password: string): Promise { await ( await this.kcAdminClient.callKcAdminClient() ).users.resetPassword({ @@ -74,7 +74,7 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return id; } - async findAccountById(id: string): Promise { + public async findAccountById(id: string): Promise { const keycloakUser = await (await this.kcAdminClient.callKcAdminClient()).users.findOne({ id }); if (!keycloakUser) { throw new Error(`Account '${id}' not found`); @@ -82,7 +82,7 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return this.extractAccount(keycloakUser); } - async findAccountByDbcAccountId(accountDbcAccountId: string): Promise { + public async findAccountByDbcAccountId(accountDbcAccountId: string): Promise { const keycloakUsers = await ( await this.kcAdminClient.callKcAdminClient() ).users.find({ q: `dbcAccountId:${accountDbcAccountId} }` }); @@ -96,7 +96,7 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return this.extractAccount(keycloakUsers[0]); } - async findAccountByDbcUserId(accountDbcUserId: string): Promise { + public async findAccountByDbcUserId(accountDbcUserId: string): Promise { const keycloakUsers = await ( await this.kcAdminClient.callKcAdminClient() ).users.find({ q: `dbcUserId:${accountDbcUserId} }` }); @@ -111,7 +111,7 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return this.extractAccount(keycloakUsers[0]); } - async findAccountsByUsername(username: string, options?: SearchOptions): Promise> { + public async findAccountsByUsername(username: string, options?: SearchOptions): Promise> { const kc = await this.kcAdminClient.callKcAdminClient(); const total = await kc.users.count({ username }); const results = await kc.users.find({ @@ -124,17 +124,17 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return [accounts, total]; } - async getAllAccounts(): Promise { + public async getAllAccounts(): Promise { const keycloakUsers = await (await this.kcAdminClient.callKcAdminClient()).users.find(); return keycloakUsers.map((user: UserRepresentation) => this.extractAccount(user)); } - async deleteAccountById(id: string): Promise { + public async deleteAccountById(id: string): Promise { await (await this.kcAdminClient.callKcAdminClient()).users.del({ id }); return id; } - async getUserAttribute( + public async getUserAttribute( userId: string, attributeName: string ): Promise { @@ -150,7 +150,7 @@ export class KeycloakIdentityManagementService extends IdentityManagementService return null; } - async setUserAttribute( + public async setUserAttribute( userId: string, attributeName: string, attributeValue: TValue diff --git a/apps/server/src/infra/sync/tsp/tsp-legacy-migration.service.integration.spec.ts b/apps/server/src/infra/sync/tsp/tsp-legacy-migration.service.integration.spec.ts index 71e441cef86..7b49b782f97 100644 --- a/apps/server/src/infra/sync/tsp/tsp-legacy-migration.service.integration.spec.ts +++ b/apps/server/src/infra/sync/tsp/tsp-legacy-migration.service.integration.spec.ts @@ -21,7 +21,7 @@ describe('account repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [SchoolEntity] })], providers: [ TspLegacyMigrationService, { diff --git a/apps/server/src/migrations/mikro-orm/Migration20240315140224.ts b/apps/server/src/migrations/mikro-orm/Migration20240315140224.ts index dcda3ce9848..aa4b5a77bea 100644 --- a/apps/server/src/migrations/mikro-orm/Migration20240315140224.ts +++ b/apps/server/src/migrations/mikro-orm/Migration20240315140224.ts @@ -5,7 +5,7 @@ enum FileRecordParentType { 'Grading' = 'gradings', } -export enum RoleName { +enum RoleName { TEACHER = 'teacher', } diff --git a/apps/server/src/modules/account/account.module.spec.ts b/apps/server/src/modules/account/account.module.spec.ts index ab399608d85..25642024c55 100644 --- a/apps/server/src/modules/account/account.module.spec.ts +++ b/apps/server/src/modules/account/account.module.spec.ts @@ -2,6 +2,7 @@ import { ConfigModule } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; import { MongoMemoryDatabaseModule } from '@testing/database'; import { AccountModule } from './account.module'; +import { AccountEntity } from './domain/entity/account.entity'; import { AccountService } from './domain/services/account.service'; import { AccountIdmToDoMapper, AccountIdmToDoMapperDb, AccountIdmToDoMapperIdm } from './repo/micro-orm/mapper'; @@ -12,7 +13,7 @@ describe('AccountModule', () => { module = await Test.createTestingModule({ imports: [ AccountModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity] }), ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true, @@ -38,7 +39,7 @@ describe('AccountModule', () => { moduleFeatureEnabled = await Test.createTestingModule({ imports: [ AccountModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity] }), ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true, @@ -70,7 +71,7 @@ describe('AccountModule', () => { moduleFeatureDisabled = await Test.createTestingModule({ imports: [ AccountModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity] }), ConfigModule.forRoot({ ignoreEnvFile: true, ignoreEnvVars: true, diff --git a/apps/server/src/modules/account/domain/services/account-idm.service.spec.ts b/apps/server/src/modules/account/domain/services/account-idm.service.spec.ts index f7995264d34..2c3a4c9e1ca 100644 --- a/apps/server/src/modules/account/domain/services/account-idm.service.spec.ts +++ b/apps/server/src/modules/account/domain/services/account-idm.service.spec.ts @@ -7,7 +7,6 @@ import { ConfigService } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; import { EntityNotFoundError } from '@shared/common/error'; import { IdmAccount } from '@shared/domain/interface'; -import { MongoMemoryDatabaseModule } from '@testing/database'; import { Account, AccountSave } from '..'; import { AccountConfig } from '../../account-config'; import { AccountIdmToDoMapper, AccountIdmToDoMapperDb } from '../../repo/micro-orm/mapper'; @@ -36,7 +35,7 @@ describe('AccountIdmService', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [], providers: [ AccountServiceIdm, { diff --git a/apps/server/src/modules/account/domain/services/account.service.integration.spec.ts b/apps/server/src/modules/account/domain/services/account.service.integration.spec.ts index 838cbd8e388..d589396b066 100644 --- a/apps/server/src/modules/account/domain/services/account.service.integration.spec.ts +++ b/apps/server/src/modules/account/domain/services/account.service.integration.spec.ts @@ -74,7 +74,7 @@ describe('AccountService Integration', () => { module = await Test.createTestingModule({ imports: [ IdentityManagementModule, - MongoMemoryDatabaseModule.forRoot(), + MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity] }), ConfigModule.forRoot({ isGlobal: true, ignoreEnvFile: true, diff --git a/apps/server/src/modules/account/repo/micro-orm/account.repo.integration.spec.ts b/apps/server/src/modules/account/repo/micro-orm/account.repo.integration.spec.ts index ef6f856ad22..6ca91abdb0c 100644 --- a/apps/server/src/modules/account/repo/micro-orm/account.repo.integration.spec.ts +++ b/apps/server/src/modules/account/repo/micro-orm/account.repo.integration.spec.ts @@ -24,7 +24,7 @@ describe('AccountRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [AccountEntity, User] })], providers: [AccountTestRepo], }).compile(); repo = module.get(AccountTestRepo); diff --git a/apps/server/src/modules/authentication/mapper/current-user.mapper.spec.ts b/apps/server/src/modules/authentication/mapper/current-user.mapper.spec.ts index bde332cfe21..2456def716a 100644 --- a/apps/server/src/modules/authentication/mapper/current-user.mapper.spec.ts +++ b/apps/server/src/modules/authentication/mapper/current-user.mapper.spec.ts @@ -44,7 +44,7 @@ describe('CurrentUserMapper', () => { accountId, systemId: undefined, roles: [teacherRole.id], - schoolId: null, + schoolId: user.school.id, isExternalUser: false, }); }); @@ -70,7 +70,7 @@ describe('CurrentUserMapper', () => { accountId, systemId: undefined, roles: [], - schoolId: null, + schoolId: user.school.id, isExternalUser: true, }); }); diff --git a/apps/server/src/modules/board/board-collaboration.app.module.ts b/apps/server/src/modules/board/board-collaboration.app.module.ts index b7ecad71cec..f77b2bd3474 100644 --- a/apps/server/src/modules/board/board-collaboration.app.module.ts +++ b/apps/server/src/modules/board/board-collaboration.app.module.ts @@ -7,12 +7,12 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity/all-entities'; import { MongoMemoryDatabaseModule } from '@testing/database'; import { AuthorizationModule } from '../authorization'; import { serverConfig } from '../server'; import { config } from './board-collaboration.config'; import { BoardWsApiModule } from './board-ws-api.module'; +import { ENTITIES, TEST_ENTITIES } from './board.entity.imports'; import { BoardModule } from './board.module'; const imports = [ @@ -34,7 +34,7 @@ const imports = [ clientUrl: DB_URL, // TODO: check if this needs to be different password: DB_PASSWORD, user: DB_USERNAME, - entities: ALL_ENTITIES, + entities: ENTITIES, }), ], providers: [], @@ -52,7 +52,7 @@ const testConfig = () => { ConfigModule.forRoot(createConfigModuleOptions(testConfig)), MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, - entities: ALL_ENTITIES, + entities: TEST_ENTITIES, }), ], }) diff --git a/apps/server/src/modules/board/board.entity.imports.ts b/apps/server/src/modules/board/board.entity.imports.ts new file mode 100644 index 00000000000..8baec7b2546 --- /dev/null +++ b/apps/server/src/modules/board/board.entity.imports.ts @@ -0,0 +1,101 @@ +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; + +export const ENTITIES = [ + AccountEntity, + BoardNodeEntity, + ClassEntity, + ColumnBoardNode, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + DeletionLogEntity, + DeletionRequestEntity, + ExternalToolEntity, + ExternalToolPseudonymEntity, + FederalStateEntity, + GroupEntity, + ImportUser, + InstanceEntity, + LessonEntity, + LtiDeepLinkTokenEntity, + LtiTool, + Material, + MediaSchoolLicenseEntity, + MediaSourceEntity, + MediaUserLicenseEntity, + News, + OauthSessionTokenEntity, + PseudonymEntity, + RegistrationPinEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolLicenseEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLicenseEntity, + UserLoginMigrationEntity, + VideoConference, +]; + +export const TEST_ENTITIES = [...ENTITIES]; diff --git a/apps/server/src/modules/board/service/board-node-permission.service.ts b/apps/server/src/modules/board/service/board-node-permission.service.ts index 60d2670be42..06134458985 100644 --- a/apps/server/src/modules/board/service/board-node-permission.service.ts +++ b/apps/server/src/modules/board/service/board-node-permission.service.ts @@ -13,7 +13,7 @@ export class BoardNodePermissionService { private readonly boardNodeAuthorizableService: BoardNodeAuthorizableService ) {} - async checkPermission(userId: EntityId, boardNode: AnyBoardNode, action: Action): Promise { + public async checkPermission(userId: EntityId, boardNode: AnyBoardNode, action: Action): Promise { const requiredPermissions: Permission[] = []; const user = await this.authorizationService.getUserWithPermissions(userId); const boardNodeAuthorizable = await this.boardNodeAuthorizableService.getBoardAuthorizable(boardNode); @@ -21,7 +21,7 @@ export class BoardNodePermissionService { this.authorizationService.checkPermission(user, boardNodeAuthorizable, { action, requiredPermissions }); } - isUserBoardEditor(userId: EntityId, userBoardRoles: UserWithBoardRoles[]): boolean { + public isUserBoardEditor(userId: EntityId, userBoardRoles: UserWithBoardRoles[]): boolean { const boardDoAuthorisedUser = userBoardRoles.find((user) => user.userId === userId); if (boardDoAuthorisedUser) { @@ -31,7 +31,7 @@ export class BoardNodePermissionService { return false; } - isUserBoardReader(userId: EntityId, userBoardRoles: UserWithBoardRoles[]): boolean { + public isUserBoardReader(userId: EntityId, userBoardRoles: UserWithBoardRoles[]): boolean { const boardDoAuthorisedUser = userBoardRoles.find((user) => user.userId === userId); if (boardDoAuthorisedUser) { diff --git a/apps/server/src/modules/class/repo/classes.repo.spec.ts b/apps/server/src/modules/class/repo/classes.repo.spec.ts index 04dd6caa876..eaf3e0e0b41 100644 --- a/apps/server/src/modules/class/repo/classes.repo.spec.ts +++ b/apps/server/src/modules/class/repo/classes.repo.spec.ts @@ -20,7 +20,7 @@ describe(ClassesRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [ClassEntity] })], providers: [ClassesRepo, ClassMapper], }).compile(); diff --git a/apps/server/src/modules/common-cartridge/common-cartridge-api.app.module.ts b/apps/server/src/modules/common-cartridge/common-cartridge-api.app.module.ts index bceb4395d24..ecec935b8d7 100644 --- a/apps/server/src/modules/common-cartridge/common-cartridge-api.app.module.ts +++ b/apps/server/src/modules/common-cartridge/common-cartridge-api.app.module.ts @@ -1,10 +1,14 @@ import { CoreModule } from '@core/core.module'; +import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { AuthGuardModule, AuthGuardOptions } from '@infra/auth-guard'; import { AuthorizationClientModule } from '@infra/authorization-client'; +import { MikroOrmModule } from '@mikro-orm/nestjs'; import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; +import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; +import { User } from '@shared/domain/entity'; import { authorizationClientConfig } from '../files-storage/files-storage.config'; import { config } from './common-cartridge.config'; import { CommonCartridgeModule } from './common-cartridge.module'; @@ -18,6 +22,15 @@ import { CommonCartridgeController } from './controller/common-cartridge.control AuthGuardModule.register([AuthGuardOptions.JWT]), ConfigModule.forRoot(createConfigModuleOptions(config)), CommonCartridgeModule, + // Will remove this in the BC-8925 + MikroOrmModule.forRoot({ + ...defaultMikroOrmOptions, + type: 'mongo', + clientUrl: DB_URL, + password: DB_PASSWORD, + user: DB_USERNAME, + entities: [User], + }), ], controllers: [CommonCartridgeController], }) diff --git a/apps/server/src/modules/common-cartridge/common-cartridge.module.ts b/apps/server/src/modules/common-cartridge/common-cartridge.module.ts index dab66351deb..263b578f3b6 100644 --- a/apps/server/src/modules/common-cartridge/common-cartridge.module.ts +++ b/apps/server/src/modules/common-cartridge/common-cartridge.module.ts @@ -1,20 +1,16 @@ +import { LoggerModule } from '@core/logger'; import { Configuration } from '@hpi-schul-cloud/commons'; -import { FilesStorageClientModule } from '@infra/files-storage-client'; -import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; +import { BoardsClientModule } from '@infra/boards-client'; import { CoursesClientModule } from '@infra/courses-client'; +import { FilesStorageClientModule } from '@infra/files-storage-client'; import { RabbitMQWrapperModule } from '@infra/rabbitmq'; -import { MikroOrmModule } from '@mikro-orm/nestjs'; +import { FilesStorageClientModule as FilesMetadataClientModule } from '@modules/files-storage-client'; import { Module } from '@nestjs/common'; -import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; -import { LoggerModule } from '@core/logger'; -import { BoardsClientModule } from '@infra/boards-client'; import { CardClientModule } from './common-cartridge-client/card-client/card-client.module'; import { LessonClientModule } from './common-cartridge-client/lesson-client/lesson-client.module'; import { CourseRoomsModule } from './common-cartridge-client/room-client'; import { CommonCartridgeExportService, CommonCartridgeImportService } from './service'; import { CommonCartridgeExportMapper } from './service/common-cartridge.mapper'; -import { FilesStorageClientModule as FilesMetadataClientModule } from '../files-storage-client'; import { CommonCartridgeUc } from './uc/common-cartridge.uc'; @Module({ @@ -25,14 +21,6 @@ import { CommonCartridgeUc } from './uc/common-cartridge.uc'; LoggerModule, CoursesClientModule, BoardsClientModule, - MikroOrmModule.forRoot({ - ...defaultMikroOrmOptions, - type: 'mongo', - clientUrl: DB_URL, - password: DB_PASSWORD, - user: DB_USERNAME, - entities: ALL_ENTITIES, - }), CourseRoomsModule.register({ basePath: `${Configuration.get('API_HOST') as string}/v3/`, }), diff --git a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts index 0110a8e707b..b9a0ae08165 100644 --- a/apps/server/src/modules/deletion-console/deletion-console.app.module.ts +++ b/apps/server/src/modules/deletion-console/deletion-console.app.module.ts @@ -8,10 +8,9 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { ConsoleModule } from 'nestjs-console'; -import { FileEntity } from '../files/entity'; import { DeletionClient } from './deletion-client'; +import { ENTITIES } from './deletion-console.entity.imports'; import { DeletionExecutionConsole } from './deletion-execution.console'; import { DeletionQueueConsole } from './deletion-queue.console'; import { deletionConsoleConfig } from './deletion.config'; @@ -31,7 +30,7 @@ import { BatchDeletionUc, DeletionExecutionUc } from './uc'; password: DB_PASSWORD, user: DB_USERNAME, allowGlobalContext: true, - entities: [...ALL_ENTITIES, FileEntity], + entities: ENTITIES, // debug: true, // use it for locally debugging of queries }), AccountModule, diff --git a/apps/server/src/modules/deletion-console/deletion-console.entity.imports.ts b/apps/server/src/modules/deletion-console/deletion-console.entity.imports.ts new file mode 100644 index 00000000000..fd17c93251c --- /dev/null +++ b/apps/server/src/modules/deletion-console/deletion-console.entity.imports.ts @@ -0,0 +1,6 @@ +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { User } from '@shared/domain/entity'; +import { Role } from '@shared/domain/entity/role.entity'; + +export const ENTITIES = [Role, User, AccountEntity]; +export const TEST_ENTITIES = [...ENTITIES]; diff --git a/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts b/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts index acb93c93667..e66002a9132 100644 --- a/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts +++ b/apps/server/src/modules/deletion-console/deletion-execution.console.spec.ts @@ -4,6 +4,7 @@ import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { MongoMemoryDatabaseModule } from '@testing/database'; import { DeletionExecutionTriggerResultBuilder, TriggerDeletionExecutionOptionsBuilder } from './builder'; import { DeletionConsoleModule } from './deletion-console.app.module'; +import { TEST_ENTITIES } from './deletion-console.entity.imports'; import { DeletionExecutionConsole } from './deletion-execution.console'; import { TriggerDeletionExecutionOptions } from './interface'; import { DeletionExecutionUc } from './uc'; @@ -15,7 +16,10 @@ describe(DeletionExecutionConsole.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [DeletionConsoleModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions })], + imports: [ + DeletionConsoleModule, + MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), + ], }).compile(); console = module.get(DeletionExecutionConsole); diff --git a/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts b/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts index 1816a523b8e..5fa6b0b6ab0 100644 --- a/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts +++ b/apps/server/src/modules/deletion-console/deletion-queue.console.spec.ts @@ -6,6 +6,7 @@ import fs from 'fs'; import { PushDeleteRequestsOptionsBuilder } from './builder'; import { UnsyncedEntitiesOptionsBuilder } from './builder/unsynced-entities-options.builder'; import { DeletionConsoleModule } from './deletion-console.app.module'; +import { TEST_ENTITIES } from './deletion-console.entity.imports'; import { DeletionQueueConsole } from './deletion-queue.console'; import { BatchDeletionUc } from './uc'; @@ -16,7 +17,10 @@ describe(DeletionQueueConsole.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [DeletionConsoleModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions })], + imports: [ + DeletionConsoleModule, + MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), + ], }).compile(); console = module.get(DeletionQueueConsole); diff --git a/apps/server/src/modules/files-storage-client/files-storage-client.module.ts b/apps/server/src/modules/files-storage-client/files-storage-client.module.ts index 0bca78dd82c..a75f7c2787a 100644 --- a/apps/server/src/modules/files-storage-client/files-storage-client.module.ts +++ b/apps/server/src/modules/files-storage-client/files-storage-client.module.ts @@ -1,5 +1,5 @@ -import { Module } from '@nestjs/common'; import { LoggerModule } from '@core/logger'; +import { Module } from '@nestjs/common'; // The files-storage-client should not know the copy-helper import { CopyHelperModule } from '@modules/copy-helper/copy-helper.module'; import { CqrsModule } from '@nestjs/cqrs'; diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-security.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-security.api.spec.ts index 9f8521f753e..e3dc7c3c3dc 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-security.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-security.api.spec.ts @@ -1,49 +1,25 @@ import { createMock } from '@golevelup/ts-jest'; -import { EntityManager } from '@mikro-orm/mongodb'; +import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { ApiValidationError } from '@shared/common/error'; import { cleanupCollections } from '@testing/cleanup-collections'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; -import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; +import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; import type { Server } from 'node:net'; -import request from 'supertest'; import { FileRecord } from '../../entity'; import { FilesStorageTestModule } from '../../files-storage-test.module'; import { FileRecordParentType, StorageLocation } from '../../interface'; import { fileRecordFactory } from '../../testing'; -import { FileRecordListResponse, ScanResultParams } from '../dto'; +import { ScanResultParams } from '../dto'; const baseRouteName = '/file-security'; const scanResult: ScanResultParams = { virus_detected: false }; -class API { - app: INestApplication; - - constructor(app: INestApplication) { - this.app = app; - } - - async put(requestString: string, body: ScanResultParams) { - const response = await request(this.app.getHttpServer()) - .put(`${baseRouteName}${requestString}`) - .set('Accept', 'application/json') - .send(body); - - return { - result: response.body as FileRecordListResponse, - error: response.body as ApiValidationError, - status: response.status, - }; - } -} - describe(`${baseRouteName} (api)`, () => { let app: INestApplication; let em: EntityManager; - let api: API; let validId: string; + let testApiClient: TestApiClient; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -57,7 +33,7 @@ describe(`${baseRouteName} (api)`, () => { app = module.createNestApplication(); await app.init(); em = module.get(EntityManager); - api = new API(app); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -66,13 +42,7 @@ describe(`${baseRouteName} (api)`, () => { beforeEach(async () => { await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - - await em.persistAndFlush([user, account]); - em.clear(); - - validId = user.school.id; + validId = new ObjectId().toHexString(); }); describe('with bad request data', () => { @@ -86,7 +56,7 @@ describe(`${baseRouteName} (api)`, () => { await em.persistAndFlush(fileRecord); em.clear(); - const response = await api.put(`/update-status/wrong-token`, scanResult); + const response = await testApiClient.put(`/update-status/wrong-token`, scanResult); expect(response.status).toEqual(404); }); @@ -104,7 +74,7 @@ describe(`${baseRouteName} (api)`, () => { await em.persistAndFlush(fileRecord); em.clear(); - const response = await api.put(`/update-status/${token}`, scanResult); + const response = await testApiClient.put(`/update-status/${token}`, scanResult); const changedFileRecord = await em.findOneOrFail(FileRecord, fileRecord.id); expect(changedFileRecord.securityCheck.status).toStrictEqual('verified'); diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts index 30adbe3ae8b..be973b6e36e 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts @@ -2,14 +2,10 @@ import { createMock } from '@golevelup/ts-jest'; import { AntivirusService } from '@infra/antivirus'; import { AuthorizationClientAdapter } from '@infra/authorization-client'; import { S3ClientAdapter } from '@infra/s3-client'; -import { EntityManager } from '@mikro-orm/mongodb'; +import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common/error'; -import { cleanupCollections } from '@testing/cleanup-collections'; -import { courseFactory } from '@testing/factory/course.factory'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; @@ -32,6 +28,7 @@ jest.mock('file-type-cjs/file-type-cjs-index', () => { describe(`${baseRouteName} (api)`, () => { let app: INestApplication; let em: EntityManager; + let testApiClient: TestApiClient; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -50,6 +47,7 @@ describe(`${baseRouteName} (api)`, () => { app = module.createNestApplication(); await app.init(); em = module.get(EntityManager); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -68,32 +66,26 @@ describe(`${baseRouteName} (api)`, () => { }, }; - const apiClient = new TestApiClient(app, baseRouteName); - - return { apiClient, copyFilesParams }; + return { copyFilesParams }; }; it('should return status 401', async () => { - const { apiClient, copyFilesParams } = setup(); + const { copyFilesParams } = setup(); - const result = await apiClient.post(`/copy/school/123/users/123`, copyFilesParams); + const result = await testApiClient.post(`/copy/school/123/users/123`, copyFilesParams); expect(result.status).toEqual(401); }); }); describe('with bad request data', () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - const targetParent = courseFactory.build({ teachers: [user] }); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, targetParent, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const validId = user.school.id; - const targetParentId = targetParent.id; + const validId = new ObjectId().toHexString(); + const targetParentId = new ObjectId().toHexString(); const copyFilesParams = { target: { @@ -104,23 +96,13 @@ describe(`${baseRouteName} (api)`, () => { }, }; - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - return { apiClient, validId, copyFilesParams }; + return { loggedInClient, validId, copyFilesParams }; }; it('should return status 400 for invalid schoolId', async () => { - const { apiClient, validId, copyFilesParams } = await setup(); + const { loggedInClient, validId, copyFilesParams } = setup(); - const result = await apiClient.post(`/copy/school/123/users/${validId}`, copyFilesParams); + const result = await loggedInClient.post(`/copy/school/123/users/${validId}`, copyFilesParams); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -133,9 +115,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentId', async () => { - const { apiClient, validId, copyFilesParams } = await setup(); + const { loggedInClient, validId, copyFilesParams } = setup(); - const result = await apiClient.post(`/copy/school/${validId}/users/123`, copyFilesParams); + const result = await loggedInClient.post(`/copy/school/${validId}/users/123`, copyFilesParams); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -148,9 +130,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentType', async () => { - const { apiClient, validId, copyFilesParams } = await setup(); + const { loggedInClient, validId, copyFilesParams } = setup(); - const result = await apiClient.post(`/copy/school/${validId}/cookies/${validId}`, copyFilesParams); + const result = await loggedInClient.post(`/copy/school/${validId}/cookies/${validId}`, copyFilesParams); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -163,7 +145,7 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentType', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); const params = { target: { storageLocation: StorageLocation.SCHOOL, @@ -173,24 +155,20 @@ describe(`${baseRouteName} (api)`, () => { }, }; - const response = await apiClient.post(`/copy/school/${validId}/users/${validId}`, params); + const response = await loggedInClient.post(`/copy/school/${validId}/users/${validId}`, params); expect(response.status).toEqual(400); }); }); describe(`with valid request data`, () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - const targetParent = courseFactory.build({ teachers: [user] }); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, targetParent, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const validId = user.school.id; - const targetParentId = targetParent.id; + const validId = new ObjectId().toHexString(); + const targetParentId = new ObjectId().toHexString(); const copyFilesParams = { target: { @@ -201,30 +179,20 @@ describe(`${baseRouteName} (api)`, () => { }, }; - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - return { validId, copyFilesParams, apiClient }; + return { validId, copyFilesParams, loggedInClient }; }; it('should return right type of data', async () => { - const { validId, copyFilesParams, apiClient } = await setup(); + const { validId, copyFilesParams, loggedInClient } = setup(); - await apiClient + await loggedInClient .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test1.txt') .set('connection', 'keep-alive') .set('content-type', 'multipart/form-data; boundary=----WebKitFormBoundaryiBMuOC0HyZ3YnA20'); - const result = await apiClient.post(`/copy/school/${validId}/schools/${validId}`, copyFilesParams); + const result = await loggedInClient.post(`/copy/school/${validId}/schools/${validId}`, copyFilesParams); const response = result.body as FileRecordListResponse; expect(result.status).toEqual(201); @@ -252,33 +220,27 @@ describe(`${baseRouteName} (api)`, () => { fileNamePrefix: 'copy from', }; - const apiClient = new TestApiClient(app, baseRouteName); - - return { copyFileParams, apiClient }; + return { copyFileParams }; }; it('should return status 401', async () => { - const { apiClient, copyFileParams } = setup(); + const { copyFileParams } = setup(); - const response = await apiClient.post(`/copy/123`, copyFileParams); + const response = await testApiClient.post(`/copy/123`, copyFileParams); expect(response.status).toEqual(401); }); }); describe('with bad request data', () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - const targetParent = courseFactory.build({ teachers: [user] }); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, targetParent, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const targetParentId = targetParent.id; + const validId = new ObjectId().toHexString(); + const targetParentId = new ObjectId().toHexString(); - const validId = user.school.id; const copyFileParams = { target: { storageLocation: StorageLocation.SCHOOL, @@ -289,23 +251,13 @@ describe(`${baseRouteName} (api)`, () => { fileNamePrefix: 'copy from', }; - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - return { validId, copyFileParams, apiClient }; + return { validId, copyFileParams, loggedInClient }; }; it('should return status 400 for invalid fileRecordId', async () => { - const { apiClient, copyFileParams } = await setup(); + const { loggedInClient, copyFileParams } = setup(); - const response = await apiClient.post(`/copy/123`, copyFileParams); + const response = await loggedInClient.post(`/copy/123`, copyFileParams); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -320,17 +272,13 @@ describe(`${baseRouteName} (api)`, () => { describe(`with valid request data`, () => { const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - const targetParent = courseFactory.build({ teachers: [user] }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, targetParent, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const targetParentId = targetParent.id; + const validId = new ObjectId().toHexString(); + const targetParentId = new ObjectId().toHexString(); - const validId = user.school.id; const copyFileParams = { target: { storageLocation: StorageLocation.SCHOOL, @@ -341,18 +289,8 @@ describe(`${baseRouteName} (api)`, () => { fileNamePrefix: 'copy from', }; - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - const result = await apiClient - .post(`/upload/school/${school.id}/schools/${school.id}`) + const result = await loggedInClient + .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test1.txt') .set('connection', 'keep-alive') .set('content-type', 'multipart/form-data; boundary=----WebKitFormBoundaryiBMuOC0HyZ3YnA20'); @@ -360,21 +298,21 @@ describe(`${baseRouteName} (api)`, () => { const fileRecordId = response.id; - return { validId, copyFileParams, apiClient, fileRecordId }; + return { validId, copyFileParams, loggedInClient, fileRecordId }; }; it('should return status 200 for successful request', async () => { - const { fileRecordId, copyFileParams, apiClient } = await setup(); + const { fileRecordId, copyFileParams, loggedInClient } = await setup(); - const response = await apiClient.post(`/copy/${fileRecordId}`, copyFileParams); + const response = await loggedInClient.post(`/copy/${fileRecordId}`, copyFileParams); expect(response.status).toEqual(201); }); it('should return right type of data', async () => { - const { fileRecordId, copyFileParams, apiClient } = await setup(); + const { fileRecordId, copyFileParams, loggedInClient } = await setup(); - const result = await apiClient.post(`/copy/${fileRecordId}`, copyFileParams); + const result = await loggedInClient.post(`/copy/${fileRecordId}`, copyFileParams); const response = result.body as FileRecordResponse; expect(response).toStrictEqual({ @@ -385,7 +323,7 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return elements not equal of requested scope', async () => { - const { fileRecordId, copyFileParams, apiClient } = await setup(); + const { fileRecordId, copyFileParams, loggedInClient } = await setup(); const otherFileRecords = fileRecordFactory.buildList(3, { parentType: FileRecordParentType.School, @@ -393,7 +331,7 @@ describe(`${baseRouteName} (api)`, () => { await em.persistAndFlush(otherFileRecords); em.clear(); - const result = await apiClient.post(`/copy/${fileRecordId}`, copyFileParams); + const result = await loggedInClient.post(`/copy/${fileRecordId}`, copyFileParams); const response = result.body as FileRecordResponse; expect(response.id).not.toEqual(fileRecordId); diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-delete-files.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-delete-files.api.spec.ts index e5d684343bd..714e4e74b65 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-delete-files.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-delete-files.api.spec.ts @@ -8,9 +8,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { AuthorizationClientAdapter } from '@infra/authorization-client'; import { ApiValidationError } from '@shared/common/error'; import { EntityId } from '@shared/domain/types'; -import { cleanupCollections } from '@testing/cleanup-collections'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; @@ -34,6 +31,7 @@ jest.mock('file-type-cjs/file-type-cjs-index', () => { describe(`${baseRouteName} (api)`, () => { let app: INestApplication; let em: EntityManager; + let testApiClient: TestApiClient; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -52,6 +50,7 @@ describe(`${baseRouteName} (api)`, () => { app = module.createNestApplication(); await app.init(); em = module.get(EntityManager); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -61,42 +60,29 @@ describe(`${baseRouteName} (api)`, () => { describe('delete files of parent', () => { describe('with not authenticated user', () => { it('should return status 401', async () => { - const apiClient = new TestApiClient(app, baseRouteName); + const loggedInClient = new TestApiClient(app, baseRouteName); - const result = await apiClient.delete(`/delete/school/123/users/123`); + const result = await loggedInClient.delete(`/delete/school/123/users/123`); expect(result.status).toEqual(401); }); }); describe('with bad request data', () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - - await em.persistAndFlush([user, account]); - em.clear(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const validId = user.school.id; + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + const validId = new ObjectId().toHexString(); - return { apiClient, validId }; + return { loggedInClient, validId }; }; it('should return status 400 for invalid schoolId', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.delete(`/delete/school/123/users/${validId}`); + const result = await loggedInClient.delete(`/delete/school/123/users/${validId}`); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -109,9 +95,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentId', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.delete(`/delete/school/${validId}/users/123`); + const result = await loggedInClient.delete(`/delete/school/${validId}/users/123`); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -124,9 +110,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentType', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.delete(`/delete/school/${validId}/cookies/${validId}`); + const result = await loggedInClient.delete(`/delete/school/${validId}/cookies/${validId}`); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -140,8 +126,13 @@ describe(`${baseRouteName} (api)`, () => { }); describe(`with valid request data`, () => { - const uploadFile = async (apiClient: TestApiClient, schoolId: string, parentId: string, fileName: string) => { - const response = await apiClient + const uploadFile = async ( + loggedInClient: TestApiClient, + schoolId: string, + parentId: string, + fileName: string + ) => { + const response = await loggedInClient .post(`/upload/school/${schoolId}/schools/${parentId}`) .attach('file', Buffer.from('abcd'), fileName) .set('connection', 'keep-alive') @@ -150,37 +141,24 @@ describe(`${baseRouteName} (api)`, () => { return response.body as FileRecordResponse; }; - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const validId = user.school.id; + const validId = new ObjectId().toHexString(); jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - return { apiClient, validId }; + return { loggedInClient, validId }; }; it('should return right type of data', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - await uploadFile(apiClient, validId, validId, 'test1.txt'); + await uploadFile(loggedInClient, validId, validId, 'test1.txt'); - const result = await apiClient.delete(`/delete/school/${validId}/schools/${validId}`); + const result = await loggedInClient.delete(`/delete/school/${validId}/schools/${validId}`); const response = result.body as FileRecordListResponse; expect(result.status).toEqual(200); @@ -204,18 +182,18 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return elements of requested scope', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); const otherParentId = new ObjectId().toHexString(); const fileRecords = await Promise.all([ - await uploadFile(apiClient, validId, validId, 'test1.txt'), - await uploadFile(apiClient, validId, validId, 'test2.txt'), - await uploadFile(apiClient, validId, validId, 'test3.txt'), - await uploadFile(apiClient, validId, otherParentId, 'other1.txt'), - await uploadFile(apiClient, validId, otherParentId, 'other2.txt'), - await uploadFile(apiClient, validId, otherParentId, 'other3.txt'), + await uploadFile(loggedInClient, validId, validId, 'test1.txt'), + await uploadFile(loggedInClient, validId, validId, 'test2.txt'), + await uploadFile(loggedInClient, validId, validId, 'test3.txt'), + await uploadFile(loggedInClient, validId, otherParentId, 'other1.txt'), + await uploadFile(loggedInClient, validId, otherParentId, 'other2.txt'), + await uploadFile(loggedInClient, validId, otherParentId, 'other3.txt'), ]); - const result = await apiClient.delete(`/delete/school/${validId}/schools/${validId}`); + const result = await loggedInClient.delete(`/delete/school/${validId}/schools/${validId}`); const response = result.body as FileRecordListResponse; const resultData: FileRecordResponse[] = response.data; @@ -230,40 +208,25 @@ describe(`${baseRouteName} (api)`, () => { describe('delete single file', () => { describe('with not authenticated user', () => { it('should return status 401', async () => { - const apiClient = new TestApiClient(app, baseRouteName); - - const response = await apiClient.delete(`/delete/123`); + const response = await testApiClient.delete(`/delete/123`); expect(response.status).toEqual(401); }); }); describe('with bad request data', () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - return { apiClient }; + return { loggedInClient }; }; it('should return status 400 for invalid fileRecordId', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = setup(); - const response = await apiClient.delete(`/delete/123`); + const response = await loggedInClient.delete(`/delete/123`); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -278,46 +241,35 @@ describe(`${baseRouteName} (api)`, () => { describe(`with valid request data`, () => { const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + const validId = new ObjectId().toHexString(); - const result = await apiClient - .post(`/upload/school/${school.id}/schools/${school.id}`) + const result = await loggedInClient + .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test1.txt') .set('connection', 'keep-alive') .set('content-type', 'multipart/form-data; boundary=----WebKitFormBoundaryiBMuOC0HyZ3YnA20'); const response = result.body as FileRecordResponse; const fileRecordId = response.id; - return { apiClient, fileRecordId }; + return { loggedInClient, fileRecordId }; }; it('should return status 200 for successful request', async () => { - const { apiClient, fileRecordId } = await setup(); + const { loggedInClient, fileRecordId } = await setup(); - const response = await apiClient.delete(`/delete/${fileRecordId}`); + const response = await loggedInClient.delete(`/delete/${fileRecordId}`); expect(response.status).toEqual(200); }); it('should return right type of data', async () => { - const { apiClient, fileRecordId } = await setup(); + const { loggedInClient, fileRecordId } = await setup(); - const result = await apiClient.delete(`/delete/${fileRecordId}`); + const result = await loggedInClient.delete(`/delete/${fileRecordId}`); const response = result.body as FileRecordResponse; expect(response).toStrictEqual({ @@ -338,7 +290,7 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return elements of requested scope', async () => { - const { apiClient, fileRecordId } = await setup(); + const { loggedInClient, fileRecordId } = await setup(); const otherFileRecords = fileRecordFactory.buildList(3, { parentType: FileRecordParentType.School, }); @@ -346,7 +298,7 @@ describe(`${baseRouteName} (api)`, () => { await em.persistAndFlush(otherFileRecords); em.clear(); - const result = await apiClient.delete(`/delete/${fileRecordId}`); + const result = await loggedInClient.delete(`/delete/${fileRecordId}`); const response = result.body as FileRecordResponse; expect(response.id).toEqual(fileRecordId); diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-download-upload.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-download-upload.api.spec.ts index edc05cedde6..cf9af63c683 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-download-upload.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-download-upload.api.spec.ts @@ -6,9 +6,6 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common/error'; -import { cleanupCollections } from '@testing/cleanup-collections'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; @@ -34,6 +31,8 @@ describe('files-storage controller (API)', () => { let em: EntityManager; let s3ClientAdapter: DeepMocked; let appPort: number; + let testApiClient: TestApiClient; + const baseRouteName = '/file'; beforeAll(async () => { @@ -58,6 +57,7 @@ describe('files-storage controller (API)', () => { em = module.get(EntityManager); s3ClientAdapter = module.get(FILES_STORAGE_S3_CONNECTION); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -66,29 +66,17 @@ describe('files-storage controller (API)', () => { }); describe('upload action', () => { - const setup = async () => { + const setup = () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); - const validId = school.id; + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); + const validId = new ObjectId().toHexString(); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - return { validId, apiClient, user }; + return { validId, loggedInClient, user: studentUser }; }; const uploadFile = async (routeName: string, apiClient: TestApiClient) => { @@ -103,10 +91,10 @@ describe('files-storage controller (API)', () => { describe('with not authenticated user', () => { it('should return status 401', async () => { - const { validId } = await setup(); - const apiClient = new TestApiClient(app, baseRouteName); + const { validId } = setup(); + const loggedInClient = new TestApiClient(app, baseRouteName); - const result = await uploadFile(`/upload/school/123/users/${validId}`, apiClient); + const result = await uploadFile(`/upload/school/123/users/${validId}`, loggedInClient); expect(result.status).toEqual(401); }); @@ -114,9 +102,9 @@ describe('files-storage controller (API)', () => { describe('with bad request data', () => { it('should return status 400 for invalid schoolId', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await uploadFile(`/upload/school/123/users/${validId}`, apiClient); + const result = await uploadFile(`/upload/school/123/users/${validId}`, loggedInClient); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -129,9 +117,9 @@ describe('files-storage controller (API)', () => { }); it('should return status 400 for invalid parentId', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await uploadFile(`/upload/school/${validId}/users/123`, apiClient); + const result = await uploadFile(`/upload/school/${validId}/users/123`, loggedInClient); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -144,9 +132,9 @@ describe('files-storage controller (API)', () => { }); it('should return status 400 for invalid parentType', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await uploadFile(`/upload/school/${validId}/cookies/${validId}`, apiClient); + const result = await uploadFile(`/upload/school/${validId}/cookies/${validId}`, loggedInClient); expect(result.status).toEqual(400); }); @@ -154,17 +142,17 @@ describe('files-storage controller (API)', () => { describe(`with valid request data`, () => { it('should return status 201 for successful upload', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const response = await uploadFile(`upload/school/${validId}/schools/${validId}`, apiClient); + const response = await uploadFile(`upload/school/${validId}/schools/${validId}`, loggedInClient); expect(response.status).toEqual(201); }); it('should return the new created file record', async () => { - const { apiClient, validId, user } = await setup(); + const { loggedInClient, validId, user } = setup(); - const result = await uploadFile(`/upload/school/${validId}/schools/${validId}`, apiClient); + const result = await uploadFile(`/upload/school/${validId}/schools/${validId}`, loggedInClient); const response = result.body as FileRecord; expect(response).toStrictEqual( @@ -182,11 +170,11 @@ describe('files-storage controller (API)', () => { }); it('should set iterator number to file name if file already exist', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - await uploadFile(`/upload/school/${validId}/schools/${validId}`, apiClient); + await uploadFile(`/upload/school/${validId}/schools/${validId}`, loggedInClient); - const result = await uploadFile(`/upload/school/${validId}/schools/${validId}`, apiClient); + const result = await uploadFile(`/upload/school/${validId}/schools/${validId}`, loggedInClient); const response = result.body as FileRecord; expect(response.name).toEqual('test (1).txt'); @@ -197,60 +185,46 @@ describe('files-storage controller (API)', () => { describe('upload from url action', () => { describe('with not authenticated user', () => { const setup = () => { - const apiClient = new TestApiClient(app, baseRouteName); - const body = { url: 'http://localhost/test.txt', fileName: 'test.txt', }; - return { apiClient, body }; + return { body }; }; it('should return status 401', async () => { - const { apiClient, body } = setup(); + const { body } = setup(); - const response = await apiClient.post(`/upload-from-url/school/123/users/123`, body); + const response = await testApiClient.post(`/upload-from-url/school/123/users/123`, body); expect(response.status).toEqual(401); }); }); describe('with bad request data', () => { - const setup = async () => { + const setup = () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); - const validId = school.id; + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); + const validId = new ObjectId().toHexString(); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); const body = { url: 'http://localhost/test.txt', fileName: 'test.txt', }; - return { validId, apiClient, body }; + return { validId, loggedInClient, body }; }; it('should return status 400 for invalid schoolId', async () => { - const { apiClient, validId, body } = await setup(); + const { loggedInClient, validId, body } = setup(); - const response = await apiClient.post(`/upload-from-url/school/123/users/${validId}`, body); + const response = await loggedInClient.post(`/upload-from-url/school/123/users/${validId}`, body); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -263,9 +237,9 @@ describe('files-storage controller (API)', () => { }); it('should return status 400 for invalid parentId', async () => { - const { apiClient, validId, body } = await setup(); + const { loggedInClient, validId, body } = setup(); - const response = await apiClient.post(`/upload-from-url/school/${validId}/users/123`, body); + const response = await loggedInClient.post(`/upload-from-url/school/${validId}/users/123`, body); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -278,9 +252,9 @@ describe('files-storage controller (API)', () => { }); it('should return status 400 for invalid parentType', async () => { - const { apiClient, validId, body } = await setup(); + const { loggedInClient, validId, body } = setup(); - const response = await apiClient.post(`/upload-from-url/school/${validId}/cookies/${validId}`, body); + const response = await loggedInClient.post(`/upload-from-url/school/${validId}/cookies/${validId}`, body); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -293,9 +267,9 @@ describe('files-storage controller (API)', () => { }); it('should return status 400 for empty url and fileName', async () => { - const { validId, apiClient } = await setup(); + const { validId, loggedInClient } = setup(); - const response = await apiClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, {}); + const response = await loggedInClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, {}); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -316,30 +290,18 @@ describe('files-storage controller (API)', () => { describe(`with new file`, () => { const setup = async () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); - const validId = school.id; + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); + const validId = new ObjectId().toHexString(); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); const expectedResponse = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); s3ClientAdapter.get.mockResolvedValueOnce(expectedResponse); - const result = await apiClient + const result = await loggedInClient .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test.txt') .set('connection', 'keep-alive') @@ -350,25 +312,25 @@ describe('files-storage controller (API)', () => { url: `http://localhost:${appPort}/file/download/${response.id}/${response.name}`, fileName: 'test.txt', headers: { - authorization: `Bearer ${authValue}`, + authorization: loggedInClient.getAuthHeader(), }, }; - return { validId, apiClient, body, user }; + return { validId, loggedInClient, body, user: studentUser }; }; it('should return status 201 for successful upload', async () => { - const { validId, apiClient, body } = await setup(); + const { validId, loggedInClient, body } = await setup(); - const result = await apiClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); + const result = await loggedInClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); expect(result.status).toEqual(201); }); it('should return the new created file record', async () => { - const { validId, apiClient, body, user } = await setup(); + const { validId, loggedInClient, body, user } = await setup(); - const result = await apiClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); + const result = await loggedInClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); const response = result.body as FileRecord; expect(response).toStrictEqual( @@ -388,32 +350,20 @@ describe('files-storage controller (API)', () => { describe(`with already existing file`, () => { const setup = async () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); - const validId = school.id; + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); + const validId = new ObjectId().toHexString(); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); const expectedResponse1 = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); const expectedResponse2 = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); s3ClientAdapter.get.mockResolvedValueOnce(expectedResponse1).mockResolvedValueOnce(expectedResponse2); - const result = await apiClient + const result = await loggedInClient .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test.txt') .set('connection', 'keep-alive') @@ -423,19 +373,19 @@ describe('files-storage controller (API)', () => { url: `http://localhost:${appPort}/file/download/${response.id}/${response.name}`, fileName: 'test.txt', headers: { - authorization: `Bearer ${authValue}`, + authorization: loggedInClient.getAuthHeader(), }, }; - await apiClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); + await loggedInClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); - return { validId, apiClient, body }; + return { validId, loggedInClient, body }; }; it('should set iterator number to file name if file already exist', async () => { - const { validId, apiClient, body } = await setup(); + const { validId, loggedInClient, body } = await setup(); - const result = await apiClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); + const result = await loggedInClient.post(`/upload-from-url/school/${validId}/schools/${validId}`, body); const response = result.body as FileRecord; expect(response.name).toEqual('test (2).txt'); @@ -447,9 +397,7 @@ describe('files-storage controller (API)', () => { describe('download action', () => { describe('with not authenticated user', () => { it('should return status 401', async () => { - const apiClient = new TestApiClient(app, baseRouteName); - - const result = await apiClient.get('/download/123/text.txt'); + const result = await testApiClient.get('/download/123/text.txt'); expect(result.status).toEqual(401); }); @@ -458,40 +406,28 @@ describe('files-storage controller (API)', () => { describe('with bad request data', () => { const setup = async () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); - const validId = school.id; + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); + const validId = new ObjectId().toHexString(); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - const result = await apiClient + const result = await loggedInClient .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test.txt') .set('connection', 'keep-alive') .set('content-type', 'multipart/form-data; boundary=----WebKitFormBoundaryiBMuOC0HyZ3YnA20'); const fileRecord = result.body as FileRecord; - return { validId, apiClient, fileRecord }; + return { validId, loggedInClient, fileRecord }; }; it('should return status 400 for invalid recordId', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = await setup(); - const result = await apiClient.get('/download/123/text.txt'); + const result = await loggedInClient.get('/download/123/text.txt'); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -504,9 +440,9 @@ describe('files-storage controller (API)', () => { }); it('should return status 404 for wrong filename', async () => { - const { apiClient, fileRecord } = await setup(); + const { loggedInClient, fileRecord } = await setup(); - const result = await apiClient.get(`/download/${fileRecord.id}/wrong-name.txt`); + const result = await loggedInClient.get(`/download/${fileRecord.id}/wrong-name.txt`); const response = result.body as ApiValidationError; expect(response.message).toEqual(ErrorType.FILE_NOT_FOUND); @@ -514,10 +450,10 @@ describe('files-storage controller (API)', () => { }); it('should return status 404 for file not found', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = await setup(); const notExistingId = new ObjectId().toHexString(); - const response = await apiClient.get(`/download/${notExistingId}/wrong-name.txt`); + const response = await loggedInClient.get(`/download/${notExistingId}/wrong-name.txt`); expect(response.status).toEqual(404); }); @@ -527,27 +463,15 @@ describe('files-storage controller (API)', () => { describe('when mimetype is not application/pdf', () => { const setup = async () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - await em.persistAndFlush([user, school, account]); - em.clear(); - const validId = school.id; + const validId = new ObjectId().toHexString(); jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - const result = await apiClient + const result = await loggedInClient .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test.txt') .set('connection', 'keep-alive') @@ -558,21 +482,21 @@ describe('files-storage controller (API)', () => { s3ClientAdapter.get.mockResolvedValueOnce(expectedResponse); - return { uploadedFile, apiClient }; + return { uploadedFile, loggedInClient }; }; it('should return status 200 for successful download', async () => { - const { uploadedFile, apiClient } = await setup(); + const { uploadedFile, loggedInClient } = await setup(); - const response = await apiClient.get(`/download/${uploadedFile.id}/${uploadedFile.name}`); + const response = await loggedInClient.get(`/download/${uploadedFile.id}/${uploadedFile.name}`); expect(response.status).toEqual(200); }); it('should return status 206 and required headers for the successful partial file stream download', async () => { - const { uploadedFile, apiClient } = await setup(); + const { uploadedFile, loggedInClient } = await setup(); - const response = await apiClient + const response = await loggedInClient .get(`/download/${uploadedFile.id}/${uploadedFile.name}`) .set('Range', 'bytes=0-'); const headers = response.headers as Record; @@ -584,9 +508,9 @@ describe('files-storage controller (API)', () => { }); it('should set content-disposition header to attachment', async () => { - const { uploadedFile, apiClient } = await setup(); + const { uploadedFile, loggedInClient } = await setup(); - const response = await apiClient.get(`/download/${uploadedFile.id}/${uploadedFile.name}`); + const response = await loggedInClient.get(`/download/${uploadedFile.id}/${uploadedFile.name}`); const headers = response.headers as Record; expect(headers['content-disposition']).toMatch('attachment'); @@ -596,27 +520,15 @@ describe('files-storage controller (API)', () => { describe('when mimetype is application/pdf', () => { const setup = async () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - await em.persistAndFlush([user, school, account]); - em.clear(); - const validId = school.id; + const validId = new ObjectId().toHexString(); jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - const result = await apiClient + const result = await loggedInClient .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test.txt') .set('connection', 'keep-alive') @@ -627,13 +539,13 @@ describe('files-storage controller (API)', () => { s3ClientAdapter.get.mockResolvedValueOnce(expectedResponse); - return { uploadedFile, apiClient }; + return { uploadedFile, loggedInClient }; }; it('should set content-disposition to inline', async () => { - const { uploadedFile, apiClient } = await setup(); + const { uploadedFile, loggedInClient } = await setup(); - const response = await apiClient.get(`/download/${uploadedFile.id}/${uploadedFile.name}`); + const response = await loggedInClient.get(`/download/${uploadedFile.id}/${uploadedFile.name}`); const headers = response.headers as Record; expect(headers['content-disposition']).toMatch('inline'); @@ -644,34 +556,21 @@ describe('files-storage controller (API)', () => { describe('file-security.download()', () => { describe('with bad request data', () => { - const setup = async () => { + const setup = () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, '', authValue); - - return { apiClient }; + return { loggedInClient }; }; it('should return status 404 for wrong token', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = setup(); - const response = await apiClient.get('/file-security/download/test-token'); + const response = await loggedInClient.get('/file-security/download/test-token'); expect(response.status).toEqual(404); }); @@ -680,31 +579,19 @@ describe('files-storage controller (API)', () => { describe(`with valid request data`, () => { const setup = async () => { jest.resetAllMocks(); - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, school, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const validId = school.id; + const validId = new ObjectId().toHexString(); jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const fileApiClient = new TestApiClient(app, baseRouteName, authValue); - const apiClient = new TestApiClient(app, '', authValue); + const fileApiClient = new TestApiClient(app, ''); const expectedResponse = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); s3ClientAdapter.get.mockResolvedValueOnce(expectedResponse); - const result = await fileApiClient + const result = await loggedInClient .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test.txt') .set('connection', 'keep-alive') @@ -713,13 +600,15 @@ describe('files-storage controller (API)', () => { const newRecord = await em.findOneOrFail(FileRecord, { id: fileRecord.id }); - return { newRecord, apiClient }; + return { newRecord, fileApiClient }; }; it('should return status 200 for successful download', async () => { - const { newRecord, apiClient } = await setup(); + const { newRecord, fileApiClient } = await setup(); - const response = await apiClient.get(`/file-security/download/${newRecord.securityCheck.requestToken || ''}`); + const response = await fileApiClient.get( + `/file-security/download/${newRecord.securityCheck.requestToken || ''}` + ); expect(response.status).toEqual(200); }); diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-list-files.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-list-files.api.spec.ts index 7b3c83e3973..a9c9a2551ec 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-list-files.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-list-files.api.spec.ts @@ -1,13 +1,10 @@ import { createMock } from '@golevelup/ts-jest'; import { AuthorizationClientAdapter } from '@infra/authorization-client'; -import { EntityManager } from '@mikro-orm/mongodb'; +import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common/error'; import { EntityId } from '@shared/domain/types'; -import { cleanupCollections } from '@testing/cleanup-collections'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; @@ -23,6 +20,7 @@ const baseRouteName = '/file/list'; describe(`${baseRouteName} (api)`, () => { let app: INestApplication; let em: EntityManager; + let testApiClient: TestApiClient; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -37,6 +35,7 @@ describe(`${baseRouteName} (api)`, () => { app = module.createNestApplication(); await app.init(); em = module.get(EntityManager); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -45,41 +44,26 @@ describe(`${baseRouteName} (api)`, () => { describe('with not authenticated user', () => { it('should return status 401', async () => { - const apiClient = new TestApiClient(app, baseRouteName); - - const response = await apiClient.get(`/school/123/users/123`); + const response = await testApiClient.get(`/school/123/users/123`); expect(response.status).toEqual(401); }); }); describe('with bad request data', () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const validId = user.school.id; + const validId = new ObjectId().toHexString(); - return { apiClient, validId }; + return { loggedInClient, validId }; }; it('should return status 400 for invalid schoolId', async () => { - const { apiClient, validId } = await setup(); - const response = await apiClient.get(`/school/123/users/${validId}`); + const { loggedInClient, validId } = setup(); + const response = await loggedInClient.get(`/school/123/users/${validId}`); const { validationErrors } = response.body as ApiValidationError; expect(response.status).toEqual(400); @@ -92,8 +76,8 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentId', async () => { - const { apiClient, validId } = await setup(); - const response = await apiClient.get(`/school/${validId}/users/123`); + const { loggedInClient, validId } = setup(); + const response = await loggedInClient.get(`/school/${validId}/users/123`); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -106,8 +90,8 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentType', async () => { - const { apiClient, validId } = await setup(); - const response = await apiClient.get(`/school/${validId}/cookies/${validId}`); + const { loggedInClient, validId } = setup(); + const response = await loggedInClient.get(`/school/${validId}/cookies/${validId}`); const { validationErrors } = response.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -121,41 +105,28 @@ describe(`${baseRouteName} (api)`, () => { }); describe(`with valid request data`, () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const validId = user.school.id; + const validId = new ObjectId().toHexString(); - return { apiClient, validId }; + return { loggedInClient, validId }; }; it('should return status 200 for successful request', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const response = await apiClient.get(`/school/${validId}/schools/${validId}`); + const response = await loggedInClient.get(`/school/${validId}/schools/${validId}`); expect(response.status).toEqual(200); }); it('should return a paginated result as default', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.get(`/school/${validId}/schools/${validId}`); + const result = await loggedInClient.get(`/school/${validId}/schools/${validId}`); const response = result.body as FileRecordListResponse; expect(response).toEqual({ @@ -167,9 +138,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should pass the pagination qurey params', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.get(`/school/${validId}/schools/${validId}`).query({ limit: 100, skip: 100 }); + const result = await loggedInClient.get(`/school/${validId}/schools/${validId}`).query({ limit: 100, skip: 100 }); const response = result.body as FileRecordListResponse; expect(response.limit).toEqual(100); @@ -177,7 +148,7 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return right type of data', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); const fileRecords = fileRecordFactory.buildList(1, { storageLocation: StorageLocation.SCHOOL, storageLocationId: validId, @@ -188,7 +159,7 @@ describe(`${baseRouteName} (api)`, () => { await em.persistAndFlush(fileRecords); em.clear(); - const result = await apiClient.get(`/school/${validId}/schools/${validId}`); + const result = await loggedInClient.get(`/school/${validId}/schools/${validId}`); const response = result.body as FileRecordListResponse; expect(Array.isArray(response.data)).toBe(true); @@ -210,7 +181,7 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return elements of requested scope', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); const fileRecords = fileRecordFactory.buildList(3, { storageLocation: StorageLocation.SCHOOL, storageLocationId: validId, @@ -226,7 +197,7 @@ describe(`${baseRouteName} (api)`, () => { await em.persistAndFlush([...otherFileRecords, ...fileRecords]); em.clear(); - const result = await apiClient.get(`/school/${validId}/schools/${validId}`); + const result = await loggedInClient.get(`/school/${validId}/schools/${validId}`); const response = result.body as FileRecordListResponse; const resultData: FileRecordResponse[] = response.data; diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-preview.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-preview.api.spec.ts index 714caaf0dd9..64d6c40c0c1 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-preview.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-preview.api.spec.ts @@ -9,8 +9,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common/error'; import { EntityId } from '@shared/domain/types'; import { cleanupCollections } from '@testing/cleanup-collections'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; @@ -37,12 +35,15 @@ const defaultQueryParameters = { outputFormat: PreviewOutputMimeTypes.IMAGE_WEBP, }; +const baseRouteName = '/file'; + describe('File Controller (API) - preview', () => { let module: TestingModule; let app: INestApplication; let em: EntityManager; let s3ClientAdapter: DeepMocked; let antivirusService: DeepMocked; + let testApiClient: TestApiClient; let uploadPath: string; beforeAll(async () => { @@ -70,6 +71,7 @@ describe('File Controller (API) - preview', () => { em = module.get(EntityManager); s3ClientAdapter = module.get(FILES_STORAGE_S3_CONNECTION); antivirusService = module.get(AntivirusService); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -88,34 +90,23 @@ describe('File Controller (API) - preview', () => { await em.flush(); }; - const setupApiClient = async () => { - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const setupApiClient = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([school, user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, '/file', authValue); + const validId = new ObjectId().toHexString(); - const schoolId = school.id; - uploadPath = `/upload/school/${schoolId}/schools/${schoolId}`; + uploadPath = `/upload/school/${validId}/schools/${validId}`; jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); antivirusService.checkStream.mockResolvedValueOnce({ virus_detected: false }); - return apiClient; + return loggedInClient; }; - const uploadFile = async (apiClient: TestApiClient) => { - const uploadResponse = await apiClient + const uploadFile = async (loggedInClient: TestApiClient) => { + const uploadResponse = await loggedInClient .post(uploadPath) .attach('file', Buffer.from('abcd'), 'test.png') .set('connection', 'keep-alive') @@ -129,9 +120,7 @@ describe('File Controller (API) - preview', () => { describe('preview', () => { describe('with not authenticated user', () => { it('should return status 401', async () => { - const apiClient = new TestApiClient(app, '/file'); - - const response = await apiClient.get('/preview/123/test.png').query(defaultQueryParameters); + const response = await testApiClient.get('/preview/123/test.png').query(defaultQueryParameters); expect(response.status).toEqual(401); }); @@ -140,8 +129,8 @@ describe('File Controller (API) - preview', () => { describe('with bad request data', () => { describe('WHEN recordId is invalid', () => { it('should return status 400', async () => { - const apiClient = await setupApiClient(); - const response = await apiClient.get('/preview/123/test.png').query(defaultQueryParameters); + const loggedInClient = setupApiClient(); + const response = await loggedInClient.get('/preview/123/test.png').query(defaultQueryParameters); const result = response.body as ApiValidationError; expect(result.validationErrors).toEqual([ @@ -156,14 +145,14 @@ describe('File Controller (API) - preview', () => { describe('WHEN width is other than PreviewWidth Enum', () => { it('should return status 400', async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); const query = { ...defaultQueryParameters, width: 2000, }; - const response = await apiClient.get(`/preview/${uploadedFile.id}/${uploadedFile.name}`).query(query); + const response = await loggedInClient.get(`/preview/${uploadedFile.id}/${uploadedFile.name}`).query(query); const result = response.body as ApiValidationError; expect(result.validationErrors).toEqual([ @@ -178,11 +167,11 @@ describe('File Controller (API) - preview', () => { describe('WHEN output format is wrong', () => { it('should return status 400', async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); const query = { ...defaultQueryParameters, outputFormat: 'image/txt' }; - const response = await apiClient.get(`/preview/${uploadedFile.id}/${uploadedFile.name}`).query(query); + const response = await loggedInClient.get(`/preview/${uploadedFile.id}/${uploadedFile.name}`).query(query); const result = response.body as ApiValidationError; expect(result.validationErrors).toEqual([ @@ -197,11 +186,11 @@ describe('File Controller (API) - preview', () => { describe('WHEN file does not exist', () => { it('should return status 404', async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); const wrongId = new ObjectId().toString(); - const response = await apiClient + const response = await loggedInClient .get(`/preview/${wrongId}/${uploadedFile.name}`) .query(defaultQueryParameters); const result = response.body as ApiValidationError; @@ -213,14 +202,14 @@ describe('File Controller (API) - preview', () => { describe('WHEN filename is wrong', () => { it('should return status 404', async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); await setScanStatus(uploadedFile.id, ScanStatus.VERIFIED); const error = new NotFoundException(); s3ClientAdapter.get.mockRejectedValueOnce(error); - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/wrong-name.txt`) .query(defaultQueryParameters); const result = response.body as ApiValidationError; @@ -235,21 +224,21 @@ describe('File Controller (API) - preview', () => { describe('WHEN preview does already exist', () => { describe('WHEN forceUpdate is undefined', () => { const setup = async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); await setScanStatus(uploadedFile.id, ScanStatus.VERIFIED); const previewFile = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); s3ClientAdapter.get.mockResolvedValueOnce(previewFile); - return { apiClient, uploadedFile }; + return { loggedInClient, uploadedFile }; }; it('should return status 200 for successful download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); const buffer = Buffer.from('testText'); - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .query(defaultQueryParameters); const result = response.body as StreamableFile; @@ -259,9 +248,9 @@ describe('File Controller (API) - preview', () => { }); it('should return status 206 and required headers for the successful partial file stream download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .set('Range', 'bytes=0-') .query(defaultQueryParameters); @@ -275,37 +264,39 @@ describe('File Controller (API) - preview', () => { describe('WHEN forceUpdate is false', () => { const setup = async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); await setScanStatus(uploadedFile.id, ScanStatus.VERIFIED); const previewFile = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); s3ClientAdapter.get.mockResolvedValueOnce(previewFile); - return { apiClient, uploadedFile }; + return { loggedInClient, uploadedFile }; }; describe('WHEN header contains no etag', () => { it('should return status 200 for successful download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); const query = { ...defaultQueryParameters, forceUpdate: false, }; - const response = await apiClient.get(`/preview/${uploadedFile.id}/${uploadedFile.name}`).query(query); + const response = await loggedInClient + .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) + .query(query); expect(response.status).toEqual(200); }); it('should return status 206 and required headers for the successful partial file stream download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); const query = { ...defaultQueryParameters, forceUpdate: false, }; - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .set('Range', 'bytes=0-') .query(query); @@ -320,14 +311,14 @@ describe('File Controller (API) - preview', () => { describe('WHEN header contains not matching etag', () => { it('should return status 200 for successful download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); const query = { ...defaultQueryParameters, forceUpdate: false, }; const etag = 'otherTag'; - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .query(query) .set('If-None-Match', etag); @@ -338,14 +329,14 @@ describe('File Controller (API) - preview', () => { describe('WHEN header contains matching etag', () => { it('should return status 304', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); const query = { ...defaultQueryParameters, forceUpdate: false, }; const etag = 'testTag'; - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .query(query) .set('If-None-Match', etag); @@ -357,36 +348,36 @@ describe('File Controller (API) - preview', () => { describe('WHEN forceUpdate is true', () => { const setup = async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); await setScanStatus(uploadedFile.id, ScanStatus.VERIFIED); const previewFile = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); s3ClientAdapter.get.mockResolvedValueOnce(previewFile); - return { apiClient, uploadedFile }; + return { loggedInClient, uploadedFile }; }; it('should return status 200 for successful download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); const query = { ...defaultQueryParameters, forceUpdate: true, }; - const response = await apiClient.get(`/preview/${uploadedFile.id}/${uploadedFile.name}`).query(query); + const response = await loggedInClient.get(`/preview/${uploadedFile.id}/${uploadedFile.name}`).query(query); expect(response.status).toEqual(200); }); it('should return status 206 and required headers for the successful partial file stream download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); const query = { ...defaultQueryParameters, forceUpdate: true, }; - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .set('Range', 'bytes=0-') .query(query); @@ -401,21 +392,21 @@ describe('File Controller (API) - preview', () => { describe('WHEN preview does not already exist', () => { const setup = async () => { - const apiClient = await setupApiClient(); - const uploadedFile = await uploadFile(apiClient); + const loggedInClient = setupApiClient(); + const uploadedFile = await uploadFile(loggedInClient); await setScanStatus(uploadedFile.id, ScanStatus.VERIFIED); const error = new NotFoundException(); const previewFile = TestHelper.createFile({ contentRange: 'bytes 0-3/4' }); s3ClientAdapter.get.mockRejectedValueOnce(error).mockResolvedValueOnce(previewFile); - return { apiClient, uploadedFile }; + return { loggedInClient, uploadedFile }; }; it('should return status 200 for successful download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .query(defaultQueryParameters); @@ -423,9 +414,9 @@ describe('File Controller (API) - preview', () => { }); it('should return status 206 and required headers for the successful partial file stream download', async () => { - const { apiClient, uploadedFile } = await setup(); + const { loggedInClient, uploadedFile } = await setup(); - const response = await apiClient + const response = await loggedInClient .get(`/preview/${uploadedFile.id}/${uploadedFile.name}`) .set('Range', 'bytes=0-') .query(defaultQueryParameters); diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-rename-file.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-rename-file.api.spec.ts index 81180650bd1..91273bbb097 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-rename-file.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-rename-file.api.spec.ts @@ -1,12 +1,9 @@ import { createMock } from '@golevelup/ts-jest'; import { AuthorizationClientAdapter } from '@infra/authorization-client'; -import { EntityManager } from '@mikro-orm/mongodb'; +import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common/error'; -import { cleanupCollections } from '@testing/cleanup-collections'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; @@ -20,6 +17,7 @@ const baseRouteName = '/file/rename'; describe(`${baseRouteName} (api)`, () => { let app: INestApplication; let em: EntityManager; + let testApiClient: TestApiClient; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -34,6 +32,7 @@ describe(`${baseRouteName} (api)`, () => { app = module.createNestApplication(); await app.init(); em = module.get(EntityManager); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -41,42 +40,32 @@ describe(`${baseRouteName} (api)`, () => { }); const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); + + const validId = new ObjectId().toHexString(); const fileParams = { - schoolId: school.id, - parentId: school.id, + schoolId: validId, + parentId: validId, parentType: FileRecordParentType.School, }; const fileRecords = fileRecordFactory.buildList(3, fileParams); - const fileRecord = fileRecordFactory.build({ ...fileParams, name: 'test.txt', creatorId: user.id }); + const fileRecord = fileRecordFactory.build({ ...fileParams, name: 'test.txt', creatorId: studentUser.id }); fileRecords.push(fileRecord); - await em.persistAndFlush([user, ...fileRecords, school]); + await em.persistAndFlush(fileRecords); em.clear(); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - return { user, fileRecord, apiClient }; + return { user: studentUser, fileRecord, loggedInClient }; }; describe('with not authenticated user', () => { it('should return status 401', async () => { - const apiClient = new TestApiClient(app, baseRouteName); + const loggedInClient = new TestApiClient(app, baseRouteName); - const result = await apiClient.patch(`invalid_id`, { fileName: 'test_new_name.txt' }); + const result = await loggedInClient.patch(`invalid_id`, { fileName: 'test_new_name.txt' }); expect(result.status).toEqual(401); }); @@ -84,9 +73,9 @@ describe(`${baseRouteName} (api)`, () => { describe('with bad request data', () => { it('should return status 400 for invalid fileRecordId', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = await setup(); - const result = await apiClient.patch(`invalid_id`, { fileName: 'test_new_name.txt' }); + const result = await loggedInClient.patch(`invalid_id`, { fileName: 'test_new_name.txt' }); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -99,9 +88,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for empty filename', async () => { - const { apiClient, fileRecord } = await setup(); + const { loggedInClient, fileRecord } = await setup(); - const result = await apiClient.patch(`${fileRecord.id}`, { fileName: undefined }); + const result = await loggedInClient.patch(`${fileRecord.id}`, { fileName: undefined }); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -114,9 +103,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 409 if filename exists', async () => { - const { apiClient, fileRecord } = await setup(); + const { loggedInClient, fileRecord } = await setup(); - const result = await apiClient.patch(`${fileRecord.id}`, { fileName: 'test.txt' }); + const result = await loggedInClient.patch(`${fileRecord.id}`, { fileName: 'test.txt' }); expect(result.body).toEqual({ code: 409, message: 'FILE_NAME_EXISTS', title: 'Conflict', type: 'CONFLICT' }); expect(result.status).toEqual(409); @@ -125,9 +114,9 @@ describe(`${baseRouteName} (api)`, () => { describe(`with valid request data`, () => { it('should return status 200 for successful request', async () => { - const { apiClient, fileRecord } = await setup(); + const { loggedInClient, fileRecord } = await setup(); - const result = await apiClient.patch(`${fileRecord.id}`, { fileName: 'test_1.txt' }); + const result = await loggedInClient.patch(`${fileRecord.id}`, { fileName: 'test_1.txt' }); const response = result.body as FileRecordResponse; expect(response.name).toEqual('test_1.txt'); diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-restore-files.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-restore-files.api.spec.ts index 2f500ea32d2..798afacd740 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-restore-files.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-restore-files.api.spec.ts @@ -7,9 +7,6 @@ import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common/error'; import { EntityId } from '@shared/domain/types'; -import { cleanupCollections } from '@testing/cleanup-collections'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import NodeClam from 'clamscan'; @@ -33,6 +30,7 @@ jest.mock('file-type-cjs/file-type-cjs-index', () => { describe(`${baseRouteName} (api)`, () => { let app: INestApplication; let em: EntityManager; + let testApiClient: TestApiClient; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -51,6 +49,7 @@ describe(`${baseRouteName} (api)`, () => { app = module.createNestApplication(); await app.init(); em = module.get(EntityManager); + testApiClient = new TestApiClient(app, baseRouteName); }); afterAll(async () => { @@ -60,42 +59,27 @@ describe(`${baseRouteName} (api)`, () => { describe('restore files of parent', () => { describe('with not authenticated uer', () => { it('should return status 401', async () => { - const apiClient = new TestApiClient(app, baseRouteName); - - const result = await apiClient.post(`/restore/school/123/users/123`); + const result = await testApiClient.post(`/restore/school/123/users/123`); expect(result.status).toEqual(401); }); }); describe('with bad request data', () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); - - await em.persistAndFlush([user]); - em.clear(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const validId = user.school.id; + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + const validId = new ObjectId().toHexString(); - return { validId, apiClient }; + return { validId, loggedInClient }; }; it('should return status 400 for invalid schoolId', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.post(`/restore/school/123/users/${validId}`); + const result = await loggedInClient.post(`/restore/school/123/users/${validId}`); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -108,9 +92,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentId', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.post(`/restore/school/${validId}/users/123`); + const result = await loggedInClient.post(`/restore/school/${validId}/users/123`); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -123,9 +107,9 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return status 400 for invalid parentType', async () => { - const { apiClient, validId } = await setup(); + const { loggedInClient, validId } = setup(); - const result = await apiClient.post(`/restore/school/${validId}/cookies/${validId}`); + const result = await loggedInClient.post(`/restore/school/${validId}/cookies/${validId}`); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -139,33 +123,20 @@ describe(`${baseRouteName} (api)`, () => { }); describe(`with valid request data`, () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const validId = user.school.id; + const validId = new ObjectId().toHexString(); jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - return { validId, apiClient }; + return { validId, loggedInClient }; }; - const uploadFile = async (apiClient: TestApiClient, path: string, fileName: string) => { - const result = await apiClient + const uploadFile = async (loggedInClient: TestApiClient, path: string, fileName: string) => { + const result = await loggedInClient .post(path) .attach('file', Buffer.from('abcd'), fileName) .set('connection', 'keep-alive') @@ -175,23 +146,23 @@ describe(`${baseRouteName} (api)`, () => { }; it('should return status 200 for successful request', async () => { - const { validId, apiClient } = await setup(); - await uploadFile(apiClient, `/upload/school/${validId}/schools/${validId}`, 'test1.txt'); + const { validId, loggedInClient } = setup(); + await uploadFile(loggedInClient, `/upload/school/${validId}/schools/${validId}`, 'test1.txt'); - await apiClient.delete(`/school/${validId}/schools/${validId}`); + await loggedInClient.delete(`/school/${validId}/schools/${validId}`); - const response = await apiClient.post(`/restore/school/${validId}/schools/${validId}`); + const response = await loggedInClient.post(`/restore/school/${validId}/schools/${validId}`); expect(response.status).toEqual(201); }); it('should return right type of data', async () => { - const { validId, apiClient } = await setup(); - await uploadFile(apiClient, `/upload/school/${validId}/schools/${validId}`, 'test1.txt'); + const { validId, loggedInClient } = setup(); + await uploadFile(loggedInClient, `/upload/school/${validId}/schools/${validId}`, 'test1.txt'); - await apiClient.delete(`/delete/school/${validId}/schools/${validId}`); + await loggedInClient.delete(`/delete/school/${validId}/schools/${validId}`); - const result = await apiClient.post(`/restore/school/${validId}/schools/${validId}`); + const result = await loggedInClient.post(`/restore/school/${validId}/schools/${validId}`); const response = result.body as FileRecordListResponse; expect(Array.isArray(response.data)).toBe(true); @@ -213,21 +184,21 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return elements of requested scope', async () => { - const { validId, apiClient } = await setup(); + const { validId, loggedInClient } = setup(); const otherParentId = new ObjectId().toHexString(); const fileRecords = await Promise.all([ - uploadFile(apiClient, `/upload/school/${validId}/schools/${validId}`, 'test1.txt'), - uploadFile(apiClient, `/upload/school/${validId}/schools/${validId}`, 'test2.txt'), - uploadFile(apiClient, `/upload/school/${validId}/schools/${validId}`, 'test3.txt'), - uploadFile(apiClient, `/upload/school/${validId}/schools/${otherParentId}`, 'other1.txt'), - uploadFile(apiClient, `/upload/school/${validId}/schools/${otherParentId}`, 'other3.txt'), - uploadFile(apiClient, `/upload/school/${validId}/schools/${otherParentId}`, 'other2.txt'), + uploadFile(loggedInClient, `/upload/school/${validId}/schools/${validId}`, 'test1.txt'), + uploadFile(loggedInClient, `/upload/school/${validId}/schools/${validId}`, 'test2.txt'), + uploadFile(loggedInClient, `/upload/school/${validId}/schools/${validId}`, 'test3.txt'), + uploadFile(loggedInClient, `/upload/school/${validId}/schools/${otherParentId}`, 'other1.txt'), + uploadFile(loggedInClient, `/upload/school/${validId}/schools/${otherParentId}`, 'other3.txt'), + uploadFile(loggedInClient, `/upload/school/${validId}/schools/${otherParentId}`, 'other2.txt'), ]); - await apiClient.delete(`/delete/school/${validId}/schools/${validId}`); + await loggedInClient.delete(`/delete/school/${validId}/schools/${validId}`); - const result = await apiClient.post(`/restore/school/${validId}/schools/${validId}`); + const result = await loggedInClient.post(`/restore/school/${validId}/schools/${validId}`); const response = result.body as FileRecordListResponse; const resultData: FileRecordResponse[] = response.data; @@ -241,31 +212,18 @@ describe(`${baseRouteName} (api)`, () => { describe('restore single file', () => { describe('with bad request data', () => { - const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); - - return { apiClient }; + return { loggedInClient }; }; it('should return status 400 for invalid fileRecordId', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = setup(); - const result = await apiClient.post(`/restore/123`); + const result = await loggedInClient.post(`/restore/123`); const { validationErrors } = result.body as ApiValidationError; expect(validationErrors).toEqual([ @@ -282,26 +240,15 @@ describe(`${baseRouteName} (api)`, () => { let fileRecordId: string; const setup = async () => { - await cleanupCollections(em); - const school = schoolEntityFactory.build(); - const { studentUser: user, studentAccount: account } = UserAndAccountTestFactory.buildStudent({ school }); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([user, account]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: account.id, - userId: user.id, - schoolId: user.school.id, - roles: [user.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, baseRouteName, authValue); + const validId = new ObjectId().toHexString(); const result = ( - await apiClient - .post(`/upload/school/${school.id}/schools/${school.id}`) + await loggedInClient + .post(`/upload/school/${validId}/schools/${validId}`) .attach('file', Buffer.from('abcd'), 'test1.txt') .set('connection', 'keep-alive') .set('content-type', 'multipart/form-data; boundary=----WebKitFormBoundaryiBMuOC0HyZ3YnA20') @@ -311,25 +258,25 @@ describe(`${baseRouteName} (api)`, () => { jest.spyOn(FileType, 'fileTypeStream').mockImplementation((readable) => Promise.resolve(readable)); - return { apiClient }; + return { loggedInClient }; }; it('should return status 200 for successful request', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = await setup(); - await apiClient.delete(`/delete/${fileRecordId}`); + await loggedInClient.delete(`/delete/${fileRecordId}`); - const response = await apiClient.post(`/restore/${fileRecordId}`); + const response = await loggedInClient.post(`/restore/${fileRecordId}`); expect(response.status).toEqual(201); }); it('should return right type of data', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = await setup(); - await apiClient.delete(`/delete/${fileRecordId}`); + await loggedInClient.delete(`/delete/${fileRecordId}`); - const result = await apiClient.post(`/restore/${fileRecordId}`); + const result = await loggedInClient.post(`/restore/${fileRecordId}`); const response = result.body as FileRecordResponse; expect(response).toStrictEqual({ @@ -349,7 +296,7 @@ describe(`${baseRouteName} (api)`, () => { }); it('should return elements of requested scope', async () => { - const { apiClient } = await setup(); + const { loggedInClient } = await setup(); const otherFileRecords = fileRecordFactory.buildList(3, { parentType: FileRecordParentType.School, }); @@ -357,9 +304,9 @@ describe(`${baseRouteName} (api)`, () => { await em.persistAndFlush(otherFileRecords); em.clear(); - await apiClient.delete(`/delete/${fileRecordId}`); + await loggedInClient.delete(`/delete/${fileRecordId}`); - const result = await apiClient.post(`/restore/${fileRecordId}`); + const result = await loggedInClient.post(`/restore/${fileRecordId}`); const response = result.body as FileRecordResponse; expect(response.id).toEqual(fileRecordId); diff --git a/apps/server/src/modules/files-storage/controller/files-storage.consumer.spec.ts b/apps/server/src/modules/files-storage/controller/files-storage.consumer.spec.ts index c5709002af1..68a7245d98a 100644 --- a/apps/server/src/modules/files-storage/controller/files-storage.consumer.spec.ts +++ b/apps/server/src/modules/files-storage/controller/files-storage.consumer.spec.ts @@ -1,19 +1,17 @@ +import { LegacyLogger } from '@core/logger'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { MikroORM } from '@mikro-orm/core'; import { ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { EntityId } from '@shared/domain/types'; -import { LegacyLogger } from '@core/logger'; -import { courseFactory } from '@testing/factory/course.factory'; import { setupEntities } from '@testing/setup-entities'; import { FileRecord } from '../entity'; import { FileRecordParentType, StorageLocation } from '../interface'; import { FilesStorageService } from '../service/files-storage.service'; import { PreviewService } from '../service/preview.service'; +import { fileRecordFactory } from '../testing'; import { CopyFilesOfParentPayload, FileRecordResponse } from './dto'; import { FilesStorageConsumer } from './files-storage.consumer'; -import { fileRecordFactory } from '../testing'; describe('FilesStorageConsumer', () => { let module: TestingModule; @@ -30,7 +28,7 @@ describe('FilesStorageConsumer', () => { }); beforeAll(async () => { - orm = await setupEntities([...ALL_ENTITIES, FileRecord]); + orm = await setupEntities([FileRecord]); module = await Test.createTestingModule({ providers: [ FilesStorageConsumer, @@ -82,18 +80,19 @@ describe('FilesStorageConsumer', () => { }); }); it('regular RPC handler should receive a valid RPC response', async () => { - const sourceCourse = courseFactory.buildWithId(); - const targetCourse = courseFactory.buildWithId(); + const sourceCourseId = new ObjectId().toHexString(); + const targetCourseId = new ObjectId().toHexString(); + const payload: CopyFilesOfParentPayload = { userId: new ObjectId().toHexString(), source: { - parentId: sourceCourse.id, + parentId: sourceCourseId, parentType: FileRecordParentType.Course, storageLocationId, storageLocation: StorageLocation.SCHOOL, }, target: { - parentId: targetCourse.id, + parentId: targetCourseId, parentType: FileRecordParentType.Course, storageLocationId, storageLocation: StorageLocation.SCHOOL, @@ -107,18 +106,18 @@ describe('FilesStorageConsumer', () => { }); describe('WHEN file not exists', () => { it('should return RpcMessage with empty array', async () => { - const sourceCourse = courseFactory.buildWithId(); - const targetCourse = courseFactory.buildWithId(); + const sourceCourseId = new ObjectId().toHexString(); + const targetCourseId = new ObjectId().toHexString(); const payload = { userId: new ObjectId().toHexString(), source: { - parentId: sourceCourse.id, + parentId: sourceCourseId, parentType: FileRecordParentType.Course, storageLocationId, storageLocation: StorageLocation.SCHOOL, }, target: { - parentId: targetCourse.id, + parentId: targetCourseId, parentType: FileRecordParentType.Course, storageLocationId, storageLocation: StorageLocation.SCHOOL, diff --git a/apps/server/src/modules/files-storage/files-storage-amqp.app.module.ts b/apps/server/src/modules/files-storage/files-storage-amqp.app.module.ts index 366740328ee..030489291c6 100644 --- a/apps/server/src/modules/files-storage/files-storage-amqp.app.module.ts +++ b/apps/server/src/modules/files-storage/files-storage-amqp.app.module.ts @@ -1,14 +1,34 @@ import { CoreModule } from '@core/core.module'; import { LoggerModule } from '@core/logger'; +import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; +import { MikroOrmModule } from '@mikro-orm/nestjs'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; +import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { FilesStorageConsumer } from './controller'; import { config } from './files-storage.config'; +import { ENTITIES } from './files-storage.entity.imports'; import { FilesStorageModule } from './files-storage.module'; @Module({ - imports: [FilesStorageModule, CoreModule, LoggerModule, ConfigModule.forRoot(createConfigModuleOptions(config))], + imports: [ + FilesStorageModule, + CoreModule, + LoggerModule, + ConfigModule.forRoot(createConfigModuleOptions(config)), + MikroOrmModule.forRoot({ + ...defaultMikroOrmOptions, + type: 'mongo', + // TODO add mongoose options as mongo options (see database.js) + clientUrl: DB_URL, + password: DB_PASSWORD, + user: DB_USERNAME, + entities: ENTITIES, + + // debug: true, // use it for locally debugging of querys + }), + ], providers: [FilesStorageConsumer], }) export class FilesStorageAMQPModule {} diff --git a/apps/server/src/modules/files-storage/files-storage-api.app.module.ts b/apps/server/src/modules/files-storage/files-storage-api.app.module.ts index b5aed77dcef..7c7d02dfa0c 100644 --- a/apps/server/src/modules/files-storage/files-storage-api.app.module.ts +++ b/apps/server/src/modules/files-storage/files-storage-api.app.module.ts @@ -1,12 +1,16 @@ import { CoreModule } from '@core/core.module'; +import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { AuthGuardModule, AuthGuardOptions } from '@infra/auth-guard'; import { AuthorizationClientModule } from '@infra/authorization-client'; +import { MikroOrmModule } from '@mikro-orm/nestjs'; import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; +import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { FileSecurityController, FilesStorageConfigController, FilesStorageController } from './controller'; import { authorizationClientConfig, config } from './files-storage.config'; +import { ENTITIES } from './files-storage.entity.imports'; import { FilesStorageModule } from './files-storage.module'; import { FilesStorageUC } from './uc'; @@ -18,6 +22,17 @@ import { FilesStorageUC } from './uc'; HttpModule, ConfigModule.forRoot(createConfigModuleOptions(config)), AuthGuardModule.register([AuthGuardOptions.JWT]), + MikroOrmModule.forRoot({ + ...defaultMikroOrmOptions, + type: 'mongo', + // TODO add mongoose options as mongo options (see database.js) + clientUrl: DB_URL, + password: DB_PASSWORD, + user: DB_USERNAME, + entities: ENTITIES, + + // debug: true, // use it for locally debugging of querys + }), ], controllers: [FilesStorageController, FilesStorageConfigController, FileSecurityController], providers: [FilesStorageUC], diff --git a/apps/server/src/modules/files-storage/files-storage-test.module.ts b/apps/server/src/modules/files-storage/files-storage-test.module.ts index 1de87091020..b325ed4a006 100644 --- a/apps/server/src/modules/files-storage/files-storage-test.module.ts +++ b/apps/server/src/modules/files-storage/files-storage-test.module.ts @@ -1,19 +1,13 @@ -import { CoreModule } from '@core/core.module'; -import { LoggerModule } from '@core/logger'; import { RabbitMQWrapperTestModule } from '@infra/rabbitmq'; -import { DynamicModule, Module } from '@nestjs/common'; -import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; -import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@testing/database'; -import { FileRecord } from './entity'; +import { Module } from '@nestjs/common'; +import { MongoMemoryDatabaseModule } from '@testing/database'; import { FilesStorageApiModule } from './files-storage-api.app.module'; +import { TEST_ENTITIES } from './files-storage.entity.imports'; const imports = [ FilesStorageApiModule, - MongoMemoryDatabaseModule.forRoot({ entities: [...ALL_ENTITIES, FileRecord] }), + MongoMemoryDatabaseModule.forRoot({ entities: TEST_ENTITIES }), RabbitMQWrapperTestModule, - CoreModule, - LoggerModule, ]; const controllers = []; const providers = []; @@ -22,13 +16,4 @@ const providers = []; controllers, providers, }) -export class FilesStorageTestModule { - static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { - return { - module: FilesStorageTestModule, - imports: [...imports, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, ...options })], - controllers, - providers, - }; - } -} +export class FilesStorageTestModule {} diff --git a/apps/server/src/modules/files-storage/files-storage.entity.imports.ts b/apps/server/src/modules/files-storage/files-storage.entity.imports.ts new file mode 100644 index 00000000000..922fa6ad1bf --- /dev/null +++ b/apps/server/src/modules/files-storage/files-storage.entity.imports.ts @@ -0,0 +1,5 @@ +import { User } from '@shared/domain/entity'; +import { FileRecord, FileRecordSecurityCheck } from './entity'; + +export const ENTITIES = [FileRecord, FileRecordSecurityCheck]; +export const TEST_ENTITIES = [...ENTITIES, User]; diff --git a/apps/server/src/modules/files-storage/files-storage.module.ts b/apps/server/src/modules/files-storage/files-storage.module.ts index 3ab25307edf..de3eed5f9af 100644 --- a/apps/server/src/modules/files-storage/files-storage.module.ts +++ b/apps/server/src/modules/files-storage/files-storage.module.ts @@ -1,15 +1,10 @@ import { LoggerModule } from '@core/logger'; import { Configuration } from '@hpi-schul-cloud/commons'; -import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { AntivirusModule } from '@infra/antivirus'; import { PreviewGeneratorProducerModule } from '@infra/preview-generator'; import { RabbitMQWrapperModule } from '@infra/rabbitmq'; import { S3ClientModule } from '@infra/s3-client'; -import { MikroOrmModule } from '@mikro-orm/nestjs'; import { Module } from '@nestjs/common'; -import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; -import { FileRecord, FileRecordSecurityCheck } from './entity'; import { s3Config } from './files-storage.config'; import { FileRecordRepo } from './repo'; import { FilesStorageService, PreviewService } from './service'; @@ -30,21 +25,7 @@ const imports = [ const providers = [FilesStorageService, PreviewService, FileRecordRepo]; @Module({ - imports: [ - ...imports, - RabbitMQWrapperModule, - MikroOrmModule.forRoot({ - ...defaultMikroOrmOptions, - type: 'mongo', - // TODO add mongoose options as mongo options (see database.js) - clientUrl: DB_URL, - password: DB_PASSWORD, - user: DB_USERNAME, - entities: [...ALL_ENTITIES, FileRecord, FileRecordSecurityCheck], - - // debug: true, // use it for locally debugging of querys - }), - ], + imports: [...imports, RabbitMQWrapperModule], providers, exports: [FilesStorageService, PreviewService], }) diff --git a/apps/server/src/modules/fwu-learning-contents/controller/api-test/fwu-learning-contents.api.spec.ts b/apps/server/src/modules/fwu-learning-contents/controller/api-test/fwu-learning-contents.api.spec.ts index f7f3018f7d9..2dca465ea9e 100644 --- a/apps/server/src/modules/fwu-learning-contents/controller/api-test/fwu-learning-contents.api.spec.ts +++ b/apps/server/src/modules/fwu-learning-contents/controller/api-test/fwu-learning-contents.api.spec.ts @@ -3,8 +3,6 @@ import { Configuration } from '@hpi-schul-cloud/commons/lib'; import { S3ClientAdapter } from '@infra/s3-client'; import { INestApplication, NotFoundException } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { JwtAuthenticationFactory } from '@testing/factory/jwt-authentication.factory'; -import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import { Readable } from 'stream'; @@ -14,6 +12,7 @@ import { FWU_CONTENT_S3_CONNECTION } from '../../fwu-learning-contents.config'; describe('FwuLearningContents Controller (api)', () => { let app: INestApplication; let s3ClientAdapter: DeepMocked; + let testApiClient: TestApiClient; beforeAll(async () => { const module = await Test.createTestingModule({ @@ -26,6 +25,7 @@ describe('FwuLearningContents Controller (api)', () => { app = module.createNestApplication(); await app.init(); s3ClientAdapter = module.get(FWU_CONTENT_S3_CONNECTION); + testApiClient = new TestApiClient(app, 'fwu'); }); afterAll(async () => { @@ -36,16 +36,8 @@ describe('FwuLearningContents Controller (api)', () => { Configuration.set('FEATURE_FWU_CONTENT_ENABLED', true); describe('when user is not authenticated', () => { - const setup = () => { - const apiClient = new TestApiClient(app, '/fwu'); - - return { apiClient }; - }; - it('should return 401 status', async () => { - const { apiClient } = setup(); - - const response = await apiClient.get('12345/example.txt'); + const response = await testApiClient.get('12345/example.txt'); expect(response.status).toEqual(401); }); @@ -67,49 +59,40 @@ describe('FwuLearningContents Controller (api)', () => { s3ClientAdapter.get.mockResolvedValueOnce(fileResponse); - const school = schoolEntityFactory.build(); - const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent({ school }); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: studentAccount.id, - userId: studentUser.id, - schoolId: studentUser.school.id, - roles: [studentUser.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, '/fwu', authValue); - - return { path, fileResponse, text, apiClient }; + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); + + return { path, fileResponse, text, loggedInClient }; }; it('should return 200 status', async () => { - const { path, apiClient } = setup(); + const { path, loggedInClient } = setup(); - const response = await apiClient.get(path); + const response = await loggedInClient.get(path); expect(response.status).toEqual(200); }); it('should return 206 status (bytesRange)', async () => { - const { path, apiClient } = setup(); + const { path, loggedInClient } = setup(); - const response = await apiClient.get(path).set('Range', '12345'); + const response = await loggedInClient.get(path).set('Range', '12345'); expect(response.status).toEqual(206); }); it('should return file content', async () => { - const { path, text, apiClient } = setup(); + const { path, text, loggedInClient } = setup(); - const response = await apiClient.get(path); + const response = await loggedInClient.get(path); expect(response.text).toEqual(text); }); it('should have the correct content-type', async () => { - const { path, fileResponse, apiClient } = setup(); + const { path, fileResponse, loggedInClient } = setup(); - const response = await apiClient.get(path); + const response = await loggedInClient.get(path); expect(response.type).toEqual(fileResponse.contentType); }); @@ -120,25 +103,16 @@ describe('FwuLearningContents Controller (api)', () => { const error = new NotFoundException('NoSuchKey'); s3ClientAdapter.get.mockRejectedValueOnce(error); - const school = schoolEntityFactory.build(); - const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent({ school }); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: studentAccount.id, - userId: studentUser.id, - schoolId: studentUser.school.id, - roles: [studentUser.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, '/fwu', authValue); - - return { apiClient }; + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); + + return { loggedInClient }; }; it('should return 404 error', async () => { - const { apiClient } = setup(); + const { loggedInClient } = setup(); - const response = await apiClient.get('1234/NotAValidKey.html'); + const response = await loggedInClient.get('1234/NotAValidKey.html'); expect(response.status).toEqual(404); }); @@ -149,27 +123,18 @@ describe('FwuLearningContents Controller (api)', () => { const error = new NotFoundException('NoSuchKey'); s3ClientAdapter.get.mockRejectedValueOnce(error); - const school = schoolEntityFactory.build(); - const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent({ school }); - const authValue = JwtAuthenticationFactory.createJwt({ - accountId: studentAccount.id, - userId: studentUser.id, - schoolId: studentUser.school.id, - roles: [studentUser.roles[0].id], - support: false, - isExternalUser: false, - }); - const apiClient = new TestApiClient(app, '/fwu', authValue); - - return { apiClient }; + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); + + return { loggedInClient }; }; it('should return InternalServerErrorException', async () => { - const { apiClient } = setup(); + const { loggedInClient } = setup(); Configuration.set('FEATURE_FWU_CONTENT_ENABLED', false); - const response = await apiClient.get('12345/example.txt'); + const response = await loggedInClient.get('12345/example.txt'); expect(response.status).toEqual(500); }); diff --git a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts index e7ab952b895..9263ec3d32c 100644 --- a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts +++ b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts @@ -3,22 +3,20 @@ import { LoggerModule } from '@core/logger'; import { AuthGuardModule, AuthGuardOptions } from '@infra/auth-guard'; import { RabbitMQWrapperTestModule } from '@infra/rabbitmq'; import { S3ClientModule } from '@infra/s3-client'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { AuthorizationModule } from '@modules/authorization'; -import { SystemEntity } from '@modules/system/entity'; import { HttpModule } from '@nestjs/axios'; -import { DynamicModule, Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; -import { Role, SchoolEntity, SchoolYearEntity, User } from '@shared/domain/entity'; -import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@testing/database'; +import { MongoMemoryDatabaseModule } from '@testing/database'; import { FwuLearningContentsController } from './controller/fwu-learning-contents.controller'; import { config, s3Config } from './fwu-learning-contents.config'; +import { TEST_ENTITIES } from './fwu.entity.imports'; import { FwuLearningContentsUc } from './uc/fwu-learning-contents.uc'; const imports = [ MongoMemoryDatabaseModule.forRoot({ - entities: [User, AccountEntity, Role, SchoolEntity, SystemEntity, SchoolYearEntity], + entities: TEST_ENTITIES, }), AuthorizationModule, ConfigModule.forRoot(createConfigModuleOptions(config)), @@ -36,13 +34,4 @@ const providers = [FwuLearningContentsUc]; controllers, providers, }) -export class FwuLearningContentsTestModule { - static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { - return { - module: FwuLearningContentsTestModule, - imports: [...imports, MongoMemoryDatabaseModule.forRoot({ ...options })], - controllers, - providers, - }; - } -} +export class FwuLearningContentsTestModule {} diff --git a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.app.module.ts b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.app.module.ts index f22c45b10c7..09ecf55060d 100644 --- a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.app.module.ts +++ b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.app.module.ts @@ -1,41 +1,22 @@ import { CoreModule } from '@core/core.module'; import { LoggerModule } from '@core/logger'; -import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { AuthGuardModule, AuthGuardOptions } from '@infra/auth-guard'; import { RabbitMQWrapperModule } from '@infra/rabbitmq'; import { S3ClientModule } from '@infra/s3-client'; -import { MikroOrmModule } from '@mikro-orm/nestjs'; -import { AccountEntity } from '@modules/account/domain/entity/account.entity'; -import { AuthorizationModule } from '@modules/authorization'; -import { SystemEntity } from '@modules/system/entity'; import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; -import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { Role, SchoolEntity, SchoolYearEntity, User } from '@shared/domain/entity'; import { FwuLearningContentsController } from './controller/fwu-learning-contents.controller'; import { config, s3Config } from './fwu-learning-contents.config'; import { FwuLearningContentsUc } from './uc/fwu-learning-contents.uc'; @Module({ imports: [ - AuthorizationModule, CoreModule, LoggerModule, HttpModule, RabbitMQWrapperModule, - MikroOrmModule.forRoot({ - ...defaultMikroOrmOptions, - type: 'mongo', - // TODO add mongoose options as mongo options (see database.js) - clientUrl: DB_URL, - password: DB_PASSWORD, - user: DB_USERNAME, - entities: [User, AccountEntity, Role, SchoolEntity, SystemEntity, SchoolYearEntity], - - // debug: true, // use it for locally debugging of querys - }), ConfigModule.forRoot(createConfigModuleOptions(config)), S3ClientModule.register([s3Config]), AuthGuardModule.register([AuthGuardOptions.JWT]), diff --git a/apps/server/src/modules/fwu-learning-contents/fwu.entity.imports.ts b/apps/server/src/modules/fwu-learning-contents/fwu.entity.imports.ts new file mode 100644 index 00000000000..a0e614bd23a --- /dev/null +++ b/apps/server/src/modules/fwu-learning-contents/fwu.entity.imports.ts @@ -0,0 +1,3 @@ +import { User } from '@shared/domain/entity'; + +export const TEST_ENTITIES = [User]; diff --git a/apps/server/src/modules/group/repo/group.repo.spec.ts b/apps/server/src/modules/group/repo/group.repo.spec.ts index 5abc82c402a..2f05c993744 100644 --- a/apps/server/src/modules/group/repo/group.repo.spec.ts +++ b/apps/server/src/modules/group/repo/group.repo.spec.ts @@ -1,9 +1,9 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; -import { type SystemEntity } from '@modules/system/entity'; +import { SystemEntity } from '@modules/system/entity'; import { systemEntityFactory } from '@modules/system/testing'; import { Test, TestingModule } from '@nestjs/testing'; import { ExternalSource, Page } from '@shared/domain/domainobject'; -import { SchoolEntity, User } from '@shared/domain/entity'; +import { Course, CourseGroup, SchoolEntity, User } from '@shared/domain/entity'; import { RoleName, SortOrder } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; import { cleanupCollections } from '@testing/cleanup-collections'; @@ -24,7 +24,11 @@ describe(GroupRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [GroupEntity, SchoolEntity, User, SystemEntity, Course, CourseGroup], + }), + ], providers: [GroupRepo], }).compile(); diff --git a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-ajax.api.spec.ts b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-ajax.api.spec.ts index 03ac25418b1..fa90f00e07f 100644 --- a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-ajax.api.spec.ts +++ b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-ajax.api.spec.ts @@ -62,15 +62,13 @@ describe('H5PEditor Controller (api)', () => { }); describe('when user is logged in', () => { - const createStudent = () => UserAndAccountTestFactory.buildStudent(); - const setup = async () => { - const { studentAccount, studentUser } = createStudent(); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const loggedInClient = await testApiClient.login(studentAccount); + await em.persistAndFlush([studentUser]); + em.clear(); return { loggedInClient, studentUser }; }; @@ -123,15 +121,13 @@ describe('H5PEditor Controller (api)', () => { }); describe('when user is logged in', () => { - const createStudent = () => UserAndAccountTestFactory.buildStudent(); - const setup = async () => { - const { studentAccount, studentUser } = createStudent(); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const loggedInClient = await testApiClient.login(studentAccount); + await em.persistAndFlush([studentUser]); + em.clear(); return { loggedInClient, studentUser }; }; diff --git a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-delete.api.spec.ts b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-delete.api.spec.ts index cc9d6be1bd6..63c2c3a6d63 100644 --- a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-delete.api.spec.ts +++ b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-delete.api.spec.ts @@ -6,7 +6,6 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { cleanupCollections } from '@testing/cleanup-collections'; -import { lessonFactory } from '@testing/factory/lesson.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import { H5PEditorTestModule } from '../../h5p-editor-test.module'; @@ -59,19 +58,16 @@ describe('H5PEditor Controller (api)', () => { describe('when user is logged in', () => { describe('when id in params is not a mongo id', () => { - const setup = async () => { - const { studentAccount, studentUser } = UserAndAccountTestFactory.buildStudent(); - - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const loggedInClient = await testApiClient.login(studentAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); return { loggedInClient }; }; it('should return 400', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); const response = await loggedInClient.post(`delete/123`); @@ -85,19 +81,16 @@ describe('H5PEditor Controller (api)', () => { }); describe('when requested content is not found', () => { - const setup = async () => { - const { studentAccount, studentUser } = UserAndAccountTestFactory.buildStudent(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); - - const loggedInClient = await testApiClient.login(studentAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); return { loggedInClient }; }; it('should return 404', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); const someId = new ObjectId().toHexString(); const response = await loggedInClient.post(`delete/${someId}`); @@ -109,13 +102,14 @@ describe('H5PEditor Controller (api)', () => { describe('when content is found', () => { const setup = async () => { const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - const lesson = lessonFactory.build(); - const h5pContent = h5pContentFactory.build({ parentId: lesson.id }); - await em.persistAndFlush([lesson, h5pContent, teacherAccount, teacherUser]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(teacherAccount, teacherUser); - const loggedInClient = await testApiClient.login(teacherAccount); + const parentId = new ObjectId().toHexString(); + const h5pContent = h5pContentFactory.build({ parentId }); + + await em.persistAndFlush([h5pContent]); + em.clear(); return { contentId: h5pContent.id, loggedInClient }; }; diff --git a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-files.api.spec.ts b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-files.api.spec.ts index 676d07ef29d..d350611dcfc 100644 --- a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-files.api.spec.ts +++ b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-files.api.spec.ts @@ -6,8 +6,6 @@ import { ContentMetadata } from '@lumieducation/h5p-server/build/src/ContentMeta import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; -import { courseFactory } from '@testing/factory/course.factory'; -import { lessonFactory } from '@testing/factory/lesson.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import { Readable } from 'stream'; @@ -124,21 +122,16 @@ describe('H5PEditor Controller (api)', () => { }); describe('when user is logged in', () => { - const createStudent = () => UserAndAccountTestFactory.buildStudent(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const setup = async () => { - const { studentAccount, studentUser } = createStudent(); - - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); - - const loggedInClient = await testApiClient.login(studentAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); return { loggedInClient }; }; it('should return the library file', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); const mockFile = { content: 'Test File', size: 9, name: 'test.txt', birthtime: new Date() }; @@ -155,7 +148,7 @@ describe('H5PEditor Controller (api)', () => { }); it('should return 404 if file does not exist', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); libraryStorage.getLibraryFile.mockRejectedValueOnce(new Error('Does not exist')); @@ -182,20 +175,17 @@ describe('H5PEditor Controller (api)', () => { }); describe('when user is logged in', () => { - const createStudent = () => UserAndAccountTestFactory.buildStudent(); - const setup = async () => { - const { studentAccount, studentUser } = createStudent(); - const course = courseFactory.build({ students: [studentUser], school: studentUser.school }); - const lesson = lessonFactory.build({ course }); - await em.persistAndFlush([studentAccount, studentUser, lesson, course]); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const content = h5pContentFactory.build({ parentId: lesson.id, parentType: H5PContentParentType.Lesson }); + const parentId = new ObjectId().toString(); + + const content = h5pContentFactory.build({ parentId, parentType: H5PContentParentType.Lesson }); await em.persistAndFlush([content]); em.clear(); - const loggedInClient = await testApiClient.login(studentAccount); - return { loggedInClient, content }; }; @@ -255,15 +245,10 @@ describe('H5PEditor Controller (api)', () => { }); describe('when user is logged in', () => { - const createStudent = () => UserAndAccountTestFactory.buildStudent(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const setup = async () => { - const { studentAccount, studentUser } = createStudent(); - - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); - - const loggedInClient = await testApiClient.login(studentAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); const mockFile = { name: 'example.txt', @@ -279,7 +264,7 @@ describe('H5PEditor Controller (api)', () => { }; it('should return the content file', async () => { - const { loggedInClient, mockFile, mockFileStats } = await setup(); + const { loggedInClient, mockFile, mockFileStats } = setup(); temporaryStorage.getFileStream.mockResolvedValueOnce(Readable.from(mockFile.content)); temporaryStorage.getFileStats.mockResolvedValueOnce(mockFileStats); @@ -291,7 +276,7 @@ describe('H5PEditor Controller (api)', () => { }); it('should work with range requests', async () => { - const { loggedInClient, mockFile, mockFileStats } = await setup(); + const { loggedInClient, mockFile, mockFileStats } = setup(); temporaryStorage.getFileStream.mockResolvedValueOnce(Readable.from(mockFile.content)); temporaryStorage.getFileStats.mockResolvedValueOnce(mockFileStats); @@ -303,7 +288,7 @@ describe('H5PEditor Controller (api)', () => { }); it('should return 404 if file does not exist', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); temporaryStorage.getFileStats.mockRejectedValueOnce(new Error('Does not exist')); @@ -330,20 +315,17 @@ describe('H5PEditor Controller (api)', () => { }); describe('when user is logged in', () => { - const createStudent = () => UserAndAccountTestFactory.buildStudent(); - const setup = async () => { - const { studentAccount, studentUser } = createStudent(); - const course = courseFactory.build({ students: [studentUser], school: studentUser.school }); - const lesson = lessonFactory.build({ course }); - await em.persistAndFlush([studentAccount, studentUser, lesson, course]); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); - const content = h5pContentFactory.build({ parentId: lesson.id, parentType: H5PContentParentType.Lesson }); + const parentId = new ObjectId().toString(); + + const content = h5pContentFactory.build({ parentId, parentType: H5PContentParentType.Lesson }); await em.persistAndFlush([content]); em.clear(); - const loggedInClient = await testApiClient.login(studentAccount); - return { loggedInClient, content }; }; diff --git a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-editor.api.spec.ts b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-editor.api.spec.ts index 5f5fd49b9d1..3e89ce9f7fb 100644 --- a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-editor.api.spec.ts +++ b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-editor.api.spec.ts @@ -6,7 +6,6 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { cleanupCollections } from '@testing/cleanup-collections'; -import { lessonFactory } from '@testing/factory/lesson.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import { H5PEditorTestModule } from '../../h5p-editor-test.module'; @@ -84,13 +83,10 @@ describe('H5PEditor Controller (api)', () => { describe('when user is logged in', () => { describe('when editor is created successfully', () => { - const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - - await em.persistAndFlush([teacherAccount, teacherUser]); - em.clear(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const loggedInClient = await testApiClient.login(teacherAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); const { editorModel } = buildContent(); h5pEditor.render.mockResolvedValueOnce(editorModel); @@ -99,7 +95,7 @@ describe('H5PEditor Controller (api)', () => { }; it('should return OK status', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); const response = await loggedInClient.get('de'); @@ -108,13 +104,10 @@ describe('H5PEditor Controller (api)', () => { }); describe('when editor throws error', () => { - const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - await em.persistAndFlush([teacherAccount, teacherUser]); - em.clear(); - - const loggedInClient = await testApiClient.login(teacherAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); h5pEditor.render.mockRejectedValueOnce(new Error('Could not get H5P editor')); @@ -122,7 +115,7 @@ describe('H5PEditor Controller (api)', () => { }; it('should return INTERNAL_SERVER_ERROR status', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); const response = await loggedInClient.get('de'); @@ -144,14 +137,15 @@ describe('H5PEditor Controller (api)', () => { describe('when user is logged in', () => { describe('when editor is returned successfully', () => { const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - const lesson = lessonFactory.build(); - const h5pContent = h5pContentFactory.build({ parentId: lesson.id }); + const { teacherUser, teacherAccount } = UserAndAccountTestFactory.buildTeacher(); - await em.persistAndFlush([teacherAccount, teacherUser, lesson, h5pContent]); - em.clear(); + const loggedInClient = testApiClient.loginByUser(teacherAccount, teacherUser); + + const parentId = new ObjectId().toHexString(); + const h5pContent = h5pContentFactory.build({ parentId }); - const loggedInClient = await testApiClient.login(teacherAccount); + await em.persistAndFlush([h5pContent]); + em.clear(); const { editorModel, exampleContent } = buildContent(); h5pEditor.render.mockResolvedValueOnce({ editorModel, content: exampleContent }); @@ -170,13 +164,10 @@ describe('H5PEditor Controller (api)', () => { }); describe('when content is not existing', () => { - const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - - await em.persistAndFlush([teacherAccount, teacherUser]); - em.clear(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const loggedInClient = await testApiClient.login(teacherAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); const { contentId } = buildContent(); @@ -184,7 +175,7 @@ describe('H5PEditor Controller (api)', () => { }; it('should return 200 status', async () => { - const { contentId, loggedInClient } = await setup(); + const { contentId, loggedInClient } = setup(); const response = await loggedInClient.get(`${contentId}/de`); @@ -193,19 +184,16 @@ describe('H5PEditor Controller (api)', () => { }); describe('when id is not a mongo id', () => { - const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - - await em.persistAndFlush([teacherAccount, teacherUser]); - em.clear(); + const setup = () => { + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); - const loggedInClient = await testApiClient.login(teacherAccount); + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); return { loggedInClient }; }; it('should return 200 status', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); const response = await loggedInClient.get(`123/de`); diff --git a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-player.api.spec.ts b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-player.api.spec.ts index 76b1b4bba77..1b9b58e4e7d 100644 --- a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-player.api.spec.ts +++ b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-get-player.api.spec.ts @@ -6,7 +6,6 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { cleanupCollections } from '@testing/cleanup-collections'; -import { lessonFactory } from '@testing/factory/lesson.factory'; import { UserAndAccountTestFactory } from '@testing/factory/user-and-account.test.factory'; import { TestApiClient } from '@testing/test-api-client'; import { H5PEditorTestModule } from '../../h5p-editor-test.module'; @@ -81,14 +80,17 @@ describe('H5PEditor Controller (api)', () => { describe('when user is not logged in', () => { describe('when content is existing', () => { const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - const lesson = lessonFactory.build(); - const h5pContent = h5pContentFactory.build({ parentId: lesson.id }); + const { teacherUser, teacherAccount } = UserAndAccountTestFactory.buildTeacher(); - await em.persistAndFlush([teacherAccount, teacherUser, lesson, h5pContent]); + const loggedInClient = testApiClient.loginByUser(teacherAccount, teacherUser); + + const parentId = new ObjectId().toHexString(); + + const h5pContent = h5pContentFactory.build({ parentId }); + + await em.persistAndFlush([h5pContent]); em.clear(); - const loggedInClient = await testApiClient.login(teacherAccount); const { playerResult } = buildContent(); h5pPlayer.render.mockResolvedValueOnce(playerResult); @@ -107,20 +109,17 @@ describe('H5PEditor Controller (api)', () => { }); describe('when content is not existing', () => { - const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); + const setup = () => { + const { teacherUser, teacherAccount } = UserAndAccountTestFactory.buildTeacher(); - await em.persistAndFlush([teacherAccount, teacherUser]); - em.clear(); - - const loggedInClient = await testApiClient.login(teacherAccount); + const loggedInClient = testApiClient.loginByUser(teacherAccount, teacherUser); const contentId = new ObjectId().toHexString(); return { loggedInClient, contentId }; }; it('should return 200 status', async () => { - const { loggedInClient, contentId } = await setup(); + const { loggedInClient, contentId } = setup(); const response = await loggedInClient.get(contentId); @@ -129,19 +128,16 @@ describe('H5PEditor Controller (api)', () => { }); describe('when id is not a mongo id', () => { - const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - - await em.persistAndFlush([teacherAccount, teacherUser]); - em.clear(); + const setup = () => { + const { teacherUser, teacherAccount } = UserAndAccountTestFactory.buildTeacher(); - const loggedInClient = await testApiClient.login(teacherAccount); + const loggedInClient = testApiClient.loginByUser(teacherAccount, teacherUser); return { loggedInClient }; }; it('should return 400', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient } = setup(); const response = await loggedInClient.get('123'); diff --git a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-save-create.api.spec.ts b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-save-create.api.spec.ts index b01c72aba40..1554fa39272 100644 --- a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-save-create.api.spec.ts +++ b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor-save-create.api.spec.ts @@ -51,7 +51,7 @@ describe('H5PEditor Controller (api)', () => { describe('create h5p content', () => { describe('with valid request params', () => { - const setup = async () => { + const setup = () => { const id = '0000000'; const metadata: IContentMetadata = { embedTypes: [], @@ -80,11 +80,9 @@ describe('H5PEditor Controller (api)', () => { library: '123', }; - const createStudent = () => UserAndAccountTestFactory.buildStudent(); - const { studentAccount, studentUser } = createStudent(); - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); - const loggedInClient = await testApiClient.login(studentAccount); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); const result1 = { id, metadata }; h5pEditor.saveOrUpdateContentReturnMetaData.mockResolvedValueOnce(result1); @@ -93,7 +91,7 @@ describe('H5PEditor Controller (api)', () => { }; it('should return CREATED status', async () => { - const { loggedInClient, params } = await setup(); + const { loggedInClient, params } = setup(); const response = await loggedInClient.post(`/edit`, params); @@ -104,7 +102,7 @@ describe('H5PEditor Controller (api)', () => { describe('save h5p content', () => { describe('when request params are valid', () => { - const setup = async () => { + const setup = () => { const contentId = new ObjectId(0); const id = '0000000'; const metadata: IContentMetadata = { @@ -133,11 +131,9 @@ describe('H5PEditor Controller (api)', () => { }, library: '123', }; - const createStudent = () => UserAndAccountTestFactory.buildStudent(); - const { studentAccount, studentUser } = createStudent(); - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); - const loggedInClient = await testApiClient.login(studentAccount); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); const result1 = { id, metadata }; h5pEditor.saveOrUpdateContentReturnMetaData.mockResolvedValueOnce(result1); @@ -146,7 +142,7 @@ describe('H5PEditor Controller (api)', () => { }; it('should return CREATED status', async () => { - const { contentId, loggedInClient, params } = await setup(); + const { contentId, loggedInClient, params } = setup(); const response = await loggedInClient.post(`/edit/${contentId.toString()}`, params); @@ -155,7 +151,7 @@ describe('H5PEditor Controller (api)', () => { }); describe('when id is not mongo id', () => { - const setup = async () => { + const setup = () => { const params: PostH5PContentCreateParams = { parentType: H5PContentParentType.Lesson, parentId: new ObjectId().toString(), @@ -173,17 +169,15 @@ describe('H5PEditor Controller (api)', () => { }, library: '123', }; - const createStudent = () => UserAndAccountTestFactory.buildStudent(); - const { studentAccount, studentUser } = createStudent(); - await em.persistAndFlush([studentAccount, studentUser]); - em.clear(); - const loggedInClient = await testApiClient.login(studentAccount); + const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent(); + + const loggedInClient = testApiClient.loginByUser(studentAccount, studentUser); return { loggedInClient, params }; }; it('should return BAD_REQUEST status', async () => { - const { loggedInClient, params } = await setup(); + const { loggedInClient, params } = setup(); const response = await loggedInClient.post(`/edit/123`, params); diff --git a/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts b/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts index b55b4ba51a5..f3d3925d6bb 100644 --- a/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts +++ b/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts @@ -6,12 +6,11 @@ import { S3ClientModule } from '@infra/s3-client'; import { AuthenticationApiTestModule } from '@modules/authentication/authentication-api-test.module'; import { UserModule } from '@modules/user'; import { DynamicModule, Module } from '@nestjs/common'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@testing/database'; import { H5PEditorController } from './controller'; -import { H5PContent } from './entity'; import { H5PEditorModule } from './h5p-editor.app.module'; import { authorizationClientConfig, s3ConfigContent, s3ConfigLibraries } from './h5p-editor.config'; +import { TEST_ENTITIES } from './h5p-editor.entity.exports'; import { H5PAjaxEndpointProvider, H5PEditorProvider, H5PPlayerProvider } from './provider'; import { H5PContentRepo, LibraryRepo } from './repo'; import { ContentStorage, LibraryStorage, TemporaryFileStorage } from './service'; @@ -19,7 +18,7 @@ import { H5PEditorUc } from './uc/h5p.uc'; const imports = [ H5PEditorModule, - MongoMemoryDatabaseModule.forRoot({ entities: [...ALL_ENTITIES, H5PContent] }), + MongoMemoryDatabaseModule.forRoot({ entities: TEST_ENTITIES }), AuthenticationApiTestModule, AuthorizationClientModule.register(authorizationClientConfig), UserModule, diff --git a/apps/server/src/modules/h5p-editor/h5p-editor.app.module.ts b/apps/server/src/modules/h5p-editor/h5p-editor.app.module.ts index 674213f161b..416edcee914 100644 --- a/apps/server/src/modules/h5p-editor/h5p-editor.app.module.ts +++ b/apps/server/src/modules/h5p-editor/h5p-editor.app.module.ts @@ -11,10 +11,9 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { H5PEditorController } from './controller/h5p-editor.controller'; -import { H5PContent, InstalledLibrary } from './entity'; import { authorizationClientConfig, config, s3ConfigContent, s3ConfigLibraries } from './h5p-editor.config'; +import { ENTITIES } from './h5p-editor.entity.exports'; import { H5PAjaxEndpointProvider, H5PEditorProvider, H5PPlayerProvider } from './provider'; import { H5PContentRepo, LibraryRepo } from './repo'; import { ContentStorage, LibraryStorage, TemporaryFileStorage } from './service'; @@ -32,9 +31,8 @@ const imports = [ clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - // Needs ALL_ENTITIES for authorization allowGlobalContext: true, - entities: [...ALL_ENTITIES, H5PContent, InstalledLibrary], + entities: ENTITIES, }), ConfigModule.forRoot(createConfigModuleOptions(config)), S3ClientModule.register([s3ConfigContent, s3ConfigLibraries]), diff --git a/apps/server/src/modules/h5p-editor/h5p-editor.entity.exports.ts b/apps/server/src/modules/h5p-editor/h5p-editor.entity.exports.ts new file mode 100644 index 00000000000..fe7d2162047 --- /dev/null +++ b/apps/server/src/modules/h5p-editor/h5p-editor.entity.exports.ts @@ -0,0 +1,5 @@ +import { User } from '@shared/domain/entity'; +import { H5PContent, InstalledLibrary } from './entity'; + +export const ENTITIES = [H5PContent, InstalledLibrary]; +export const TEST_ENTITIES = [...ENTITIES, User]; diff --git a/apps/server/src/modules/idp-console/api/idp-sync-console.spec.ts b/apps/server/src/modules/idp-console/api/idp-sync-console.spec.ts index b86450dc766..9313e383adc 100644 --- a/apps/server/src/modules/idp-console/api/idp-sync-console.spec.ts +++ b/apps/server/src/modules/idp-console/api/idp-sync-console.spec.ts @@ -3,6 +3,7 @@ import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; import { MongoMemoryDatabaseModule } from '@testing/database'; import { ObjectId } from 'bson'; import { IdpConsoleModule } from '../idp-console.app.module'; +import { TEST_ENTITIES } from '../idp.entity.imports'; import { UsersSyncOptionsBuilder } from '../testing'; import { IdpSyncConsole } from './idp-sync-console'; import { SystemType } from './interface'; @@ -16,7 +17,10 @@ describe(IdpSyncConsole.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [IdpConsoleModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions })], + imports: [ + IdpConsoleModule, + MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), + ], }).compile(); console = module.get(IdpSyncConsole); diff --git a/apps/server/src/modules/idp-console/idp-console.app.module.ts b/apps/server/src/modules/idp-console/idp-console.app.module.ts index b40ec6f6dd9..94db77e6b36 100644 --- a/apps/server/src/modules/idp-console/idp-console.app.module.ts +++ b/apps/server/src/modules/idp-console/idp-console.app.module.ts @@ -5,16 +5,16 @@ import { RabbitMQWrapperModule } from '@infra/rabbitmq'; import { SchulconnexClientModule } from '@infra/schulconnex-client/schulconnex-client.module'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { AccountModule } from '@modules/account'; -import { SynchronizationEntity, SynchronizationModule } from '@modules/synchronization'; +import { SynchronizationModule } from '@modules/synchronization'; import { UserModule } from '@modules/user'; import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import { ConsoleModule } from 'nestjs-console'; import { IdpSyncConsole, SynchronizationUc } from './api'; import { idpConsoleConfigConfig } from './idp-console.config'; +import { ENTITIES } from './idp.entity.imports'; @Module({ imports: [ @@ -28,7 +28,7 @@ import { idpConsoleConfigConfig } from './idp-console.config'; password: DB_PASSWORD, user: DB_USERNAME, allowGlobalContext: true, - entities: [...ALL_ENTITIES, SynchronizationEntity], + entities: ENTITIES, // debug: true, // use it for locally debugging of queries }), UserModule, diff --git a/apps/server/src/modules/idp-console/idp.entity.imports.ts b/apps/server/src/modules/idp-console/idp.entity.imports.ts new file mode 100644 index 00000000000..02d1a95e870 --- /dev/null +++ b/apps/server/src/modules/idp-console/idp.entity.imports.ts @@ -0,0 +1,6 @@ +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { SynchronizationEntity } from '@modules/synchronization'; +import { User } from '@shared/domain/entity'; + +export const ENTITIES = [SynchronizationEntity, User, AccountEntity]; +export const TEST_ENTITIES = [...ENTITIES]; diff --git a/apps/server/src/modules/instance/entity/instance.entity.ts b/apps/server/src/modules/instance/entity/instance.entity.ts index 93e1c1d93cc..4dd4620e3b4 100644 --- a/apps/server/src/modules/instance/entity/instance.entity.ts +++ b/apps/server/src/modules/instance/entity/instance.entity.ts @@ -1,5 +1,5 @@ import { Entity, Property } from '@mikro-orm/core'; -import { BaseEntityWithTimestamps } from '@shared/domain/entity/base.entity'; // directly imported because of circular dependencies through ALL_ENTITIES +import { BaseEntityWithTimestamps } from '@shared/domain/entity'; import { EntityId } from '@shared/domain/types'; export interface InstanceEntityProps { diff --git a/apps/server/src/modules/instance/repo/instance.repo.spec.ts b/apps/server/src/modules/instance/repo/instance.repo.spec.ts index 1fb7cbaa2bb..e9054af0fcd 100644 --- a/apps/server/src/modules/instance/repo/instance.repo.spec.ts +++ b/apps/server/src/modules/instance/repo/instance.repo.spec.ts @@ -14,7 +14,7 @@ describe(InstanceRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [InstanceEntity] })], providers: [InstanceRepo], }).compile(); diff --git a/apps/server/src/modules/learnroom/repo/mikro-orm/course.repo.integration.spec.ts b/apps/server/src/modules/learnroom/repo/mikro-orm/course.repo.integration.spec.ts index 1805e1779af..d7c2475f5ef 100644 --- a/apps/server/src/modules/learnroom/repo/mikro-orm/course.repo.integration.spec.ts +++ b/apps/server/src/modules/learnroom/repo/mikro-orm/course.repo.integration.spec.ts @@ -33,7 +33,7 @@ describe(CourseMikroOrmRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [CourseEntity, CourseGroup, User] })], providers: [{ provide: COURSE_REPO, useClass: CourseMikroOrmRepo }], }).compile(); diff --git a/apps/server/src/modules/legacy-school/repo/school-system-options.repo.spec.ts b/apps/server/src/modules/legacy-school/repo/school-system-options.repo.spec.ts index 0b35677a32d..d7a028c10ba 100644 --- a/apps/server/src/modules/legacy-school/repo/school-system-options.repo.spec.ts +++ b/apps/server/src/modules/legacy-school/repo/school-system-options.repo.spec.ts @@ -19,7 +19,9 @@ describe(SchoolSystemOptionsRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ entities: [SchoolSystemOptionsEntity, SchoolEntity, SystemEntity] }), + ], providers: [SchoolSystemOptionsRepo], }).compile(); diff --git a/apps/server/src/modules/legacy-school/repo/schoolyear.repo.integration.spec.ts b/apps/server/src/modules/legacy-school/repo/schoolyear.repo.integration.spec.ts index c7a6a9889df..03772ddaef5 100644 --- a/apps/server/src/modules/legacy-school/repo/schoolyear.repo.integration.spec.ts +++ b/apps/server/src/modules/legacy-school/repo/schoolyear.repo.integration.spec.ts @@ -13,7 +13,7 @@ describe('schoolyear repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [SchoolYearEntity] })], providers: [SchoolYearRepo], }).compile(); repo = module.get(SchoolYearRepo); diff --git a/apps/server/src/modules/lesson/repository/lesson.repo.integration.spec.ts b/apps/server/src/modules/lesson/repository/lesson.repo.integration.spec.ts index eb26e00a972..59ab7cf8d36 100644 --- a/apps/server/src/modules/lesson/repository/lesson.repo.integration.spec.ts +++ b/apps/server/src/modules/lesson/repository/lesson.repo.integration.spec.ts @@ -1,6 +1,15 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { ComponentProperties, ComponentType, LessonEntity } from '@shared/domain/entity'; +import { + ComponentProperties, + ComponentType, + Course, + CourseGroup, + LessonEntity, + Material, + Submission, + Task, +} from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { courseFactory } from '@testing/factory/course.factory'; import { lessonFactory } from '@testing/factory/lesson.factory'; @@ -18,7 +27,11 @@ describe('LessonRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [LessonEntity, Material, Task, Submission, Course, CourseGroup], + }), + ], providers: [LessonRepo], }).compile(); repo = module.get(LessonRepo); diff --git a/apps/server/src/modules/server-console/api-test/database-management.console.api.spec.ts b/apps/server/src/modules/management/console/api-test/database-management.console.api.spec.ts similarity index 95% rename from apps/server/src/modules/server-console/api-test/database-management.console.api.spec.ts rename to apps/server/src/modules/management/console/api-test/database-management.console.api.spec.ts index 4162131517a..969ba958f73 100644 --- a/apps/server/src/modules/server-console/api-test/database-management.console.api.spec.ts +++ b/apps/server/src/modules/management/console/api-test/database-management.console.api.spec.ts @@ -1,8 +1,8 @@ import { ConsoleWriterService } from '@infra/console'; -import { ServerConsoleModule } from '@modules/server-console/server-console.app.module'; import { INestApplicationContext } from '@nestjs/common'; import { CommanderError } from 'commander'; import { BootstrapConsole, ConsoleService } from 'nestjs-console'; +import { ManagementConsoleModule } from '../../management-console.app.module'; import { TestBootstrapConsole, execute } from './test-bootstrap.console'; describe('DatabaseManagementConsole (API)', () => { @@ -13,7 +13,7 @@ describe('DatabaseManagementConsole (API)', () => { beforeEach(async () => { bootstrap = new TestBootstrapConsole({ - module: ServerConsoleModule, + module: ManagementConsoleModule, useDecorators: true, }); app = await bootstrap.init(); diff --git a/apps/server/src/modules/server-console/api-test/identity-management.console.api.spec.ts b/apps/server/src/modules/management/console/api-test/identity-management.console.api.spec.ts similarity index 92% rename from apps/server/src/modules/server-console/api-test/identity-management.console.api.spec.ts rename to apps/server/src/modules/management/console/api-test/identity-management.console.api.spec.ts index 7e32f7f1e9c..b14759b79ac 100644 --- a/apps/server/src/modules/server-console/api-test/identity-management.console.api.spec.ts +++ b/apps/server/src/modules/management/console/api-test/identity-management.console.api.spec.ts @@ -1,7 +1,7 @@ -import { ServerConsoleModule } from '@modules/server-console/server-console.app.module'; import { INestApplicationContext } from '@nestjs/common'; import { CommanderError } from 'commander'; import { BootstrapConsole, ConsoleService } from 'nestjs-console'; +import { ManagementConsoleModule } from '../../management-console.app.module'; import { execute, TestBootstrapConsole } from './test-bootstrap.console'; describe.skip('IdentityManagementConsole (API)', () => { @@ -10,7 +10,7 @@ describe.skip('IdentityManagementConsole (API)', () => { let consoleService: ConsoleService; beforeAll(async () => { console = new TestBootstrapConsole({ - module: ServerConsoleModule, + module: ManagementConsoleModule, useDecorators: true, }); app = await console.init(); diff --git a/apps/server/src/modules/server-console/api-test/test-bootstrap.console.ts b/apps/server/src/modules/management/console/api-test/test-bootstrap.console.ts similarity index 90% rename from apps/server/src/modules/server-console/api-test/test-bootstrap.console.ts rename to apps/server/src/modules/management/console/api-test/test-bootstrap.console.ts index 56a41d923ae..49604769965 100644 --- a/apps/server/src/modules/server-console/api-test/test-bootstrap.console.ts +++ b/apps/server/src/modules/management/console/api-test/test-bootstrap.console.ts @@ -1,8 +1,8 @@ import { createMock } from '@golevelup/ts-jest'; import { ConsoleWriterService } from '@infra/console'; -import { DatabaseManagementUc } from '@modules/management/uc/database-management.uc'; import { Test, TestingModule } from '@nestjs/testing'; import { AbstractBootstrapConsole, BootstrapConsole } from 'nestjs-console'; +import { DatabaseManagementUc } from '../../uc/database-management.uc'; export class TestBootstrapConsole extends AbstractBootstrapConsole { create(): Promise { diff --git a/apps/server/src/modules/management/console/database-management.console.ts b/apps/server/src/modules/management/console/database-management.console.ts index 1b14fb37bd5..0947c18af83 100644 --- a/apps/server/src/modules/management/console/database-management.console.ts +++ b/apps/server/src/modules/management/console/database-management.console.ts @@ -37,7 +37,7 @@ export class DatabaseManagementConsole { ], description: 'reset database collections with seed data from filesystem', }) - async seedCollections(options: Options): Promise { + public async seedCollections(options: Options): Promise { const filter = options?.collection ? [options.collection] : undefined; const collections = options.onlyfactories @@ -64,7 +64,7 @@ export class DatabaseManagementConsole { ], description: 'export database collections to filesystem', }) - async exportCollections(options: Options): Promise { + public async exportCollections(options: Options): Promise { const filter = options?.collection ? [options.collection] : undefined; const toSeedFolder = options?.override ? true : undefined; const collections = await this.databaseManagementUc.exportCollectionsToFileSystem(filter, toSeedFolder); @@ -78,7 +78,7 @@ export class DatabaseManagementConsole { options: [], description: 'sync indexes from nest and mikroorm', }) - async syncIndexes(): Promise { + public async syncIndexes(): Promise { await this.databaseManagementUc.syncIndexes(); const report = 'sync of indexes is completed'; this.consoleWriter.info(report); @@ -121,7 +121,7 @@ export class DatabaseManagementConsole { ], description: 'Execute MikroOrm migration up/down', }) - async migration(migrationOptions: MigrationOptions): Promise { + public async migration(migrationOptions: MigrationOptions): Promise { let report = 'no migration option was given'; if (!migrationOptions.up && !migrationOptions.down && !migrationOptions.pending) { this.consoleWriter.error(report); diff --git a/apps/server/src/modules/server-console/server-console.app.module.ts b/apps/server/src/modules/management/management-console.app.module.ts similarity index 78% rename from apps/server/src/modules/server-console/server-console.app.module.ts rename to apps/server/src/modules/management/management-console.app.module.ts index fd5d1e255bf..23d1616c2be 100644 --- a/apps/server/src/modules/server-console/server-console.app.module.ts +++ b/apps/server/src/modules/management/management-console.app.module.ts @@ -10,8 +10,7 @@ import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { ConsoleModule } from 'nestjs-console'; -import { mikroOrmCliConfig } from '../../config/mikro-orm-cli.config'; -import { ServerConsole } from './server.console'; +import mikroOrmCliConfig from './mikro-orm-cli.config'; @Module({ imports: [ @@ -20,13 +19,10 @@ import { ServerConsole } from './server.console'; ConsoleWriterModule, FilesModule, ConfigModule.forRoot(createConfigModuleOptions(serverConfig)), - ...((Configuration.get('FEATURE_IDENTITY_MANAGEMENT_ENABLED') as boolean) ? [KeycloakModule] : []), + ...((Configuration.get('FEATURE_IDENTITY_MANAGEMENT_ENABLED') as boolean) ? [KeycloakModule] : []), // TODO: Was macht das KeycloakModule hier? MikroOrmModule.forRoot(mikroOrmCliConfig), SyncModule, ], - providers: [ - /** add console services as providers */ - ServerConsole, - ], + providers: [], }) -export class ServerConsoleModule {} +export class ManagementConsoleModule {} diff --git a/apps/server/src/modules/management/management-server.app.module.ts b/apps/server/src/modules/management/management-server.app.module.ts index 60bcde2a22d..1fd1c0d32b8 100644 --- a/apps/server/src/modules/management/management-server.app.module.ts +++ b/apps/server/src/modules/management/management-server.app.module.ts @@ -1,9 +1,9 @@ import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; import { MikroOrmModule } from '@mikro-orm/nestjs'; -import { DynamicModule, Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; -import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@testing/database'; +import { MongoMemoryDatabaseModule } from '@testing/database'; +import { ENTITIES, TEST_ENTITIES } from './management.entity.imports'; import { ManagementModule } from './management.module'; @Module({ @@ -16,20 +16,16 @@ import { ManagementModule } from './management.module'; clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: ALL_ENTITIES, + entities: ENTITIES, }), ], }) export class ManagementServerModule {} @Module({ - imports: [ManagementModule, MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions })], + imports: [ + ManagementModule, + MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), + ], }) -export class ManagementServerTestModule { - static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { - return { - module: ManagementModule, - imports: [MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, ...options })], - }; - } -} +export class ManagementServerTestModule {} diff --git a/apps/server/src/modules/management/management.entity.imports.ts b/apps/server/src/modules/management/management.entity.imports.ts new file mode 100644 index 00000000000..4bb6bc573ad --- /dev/null +++ b/apps/server/src/modules/management/management.entity.imports.ts @@ -0,0 +1,117 @@ +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { FileRecord } from '@modules/files-storage/entity'; +import { FileEntity } from '@modules/files/entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; + +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FileEntity, + FileRecord, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + MediaSourceEntity, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; + +export const TEST_ENTITIES = [...ENTITIES]; diff --git a/apps/server/src/modules/management/management.module.ts b/apps/server/src/modules/management/management.module.ts index fc5ee2ed584..2e78b743d3a 100644 --- a/apps/server/src/modules/management/management.module.ts +++ b/apps/server/src/modules/management/management.module.ts @@ -1,7 +1,6 @@ import { LoggerModule } from '@core/logger'; import { Configuration } from '@hpi-schul-cloud/commons/lib'; import { ConsoleWriterService } from '@infra/console'; -import { DatabaseManagementModule, DatabaseManagementService } from '@infra/database'; import { EncryptionModule } from '@infra/encryption'; import { FeathersModule } from '@infra/feathers'; import { FileSystemModule } from '@infra/file-system'; @@ -16,11 +15,11 @@ import { DatabaseManagementConsole } from './console/database-management.console import { DatabaseManagementController } from './controller/database-management.controller'; import { BsonConverter } from './converter/bson.converter'; import { MediaSourcesSeedDataService, SystemsSeedDataService } from './service'; +import { DatabaseManagementService } from './service/database-management.service'; import { DatabaseManagementUc } from './uc/database-management.uc'; const baseImports = [ FileSystemModule, - DatabaseManagementModule, LoggerModule, ConfigModule.forRoot(createConfigModuleOptions(serverConfig)), EncryptionModule, diff --git a/apps/server/src/config/mikro-orm-cli.config.ts b/apps/server/src/modules/management/mikro-orm-cli.config.ts similarity index 66% rename from apps/server/src/config/mikro-orm-cli.config.ts rename to apps/server/src/modules/management/mikro-orm-cli.config.ts index 72a5b34502b..8559b73c498 100644 --- a/apps/server/src/config/mikro-orm-cli.config.ts +++ b/apps/server/src/modules/management/mikro-orm-cli.config.ts @@ -1,24 +1,18 @@ import { DB_PASSWORD, DB_URL, DB_USERNAME } from '@imports-from-feathers'; -import type { MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs/typings'; -import { FileRecord } from '@modules/files-storage/entity'; -import { FileEntity } from '@modules/files/entity'; -import { ALL_ENTITIES } from '@shared/domain/entity'; +import { MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs'; import path from 'path'; +import { ENTITIES } from './management.entity.imports'; -const migrationsPath = path.resolve(__dirname, '..', 'migrations', 'mikro-orm'); +export const migrationsPath = path.resolve(__dirname, '..', '..', 'migrations', 'mikro-orm'); -export const mikroOrmCliConfig: MikroOrmModuleSyncOptions = { +const mikroOrmCliConfig: MikroOrmModuleSyncOptions = { // TODO repeats server module definitions type: 'mongo', clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: [...ALL_ENTITIES, FileEntity, FileRecord], + entities: ENTITIES, allowGlobalContext: true, - /* - findOneOrFailHandler: (entityName: string, where: Dictionary | IPrimaryKey) => - new NotFoundException(`The requested ${entityName}: ${JSON.stringify(where)} has not been found.`), - */ migrations: { tableName: 'migrations', // name of database table with log of executed transactions path: migrationsPath, // path to the folder with migrations diff --git a/apps/server/src/infra/database/management/database-management.service.spec.ts b/apps/server/src/modules/management/service/database-management.service.spec.ts similarity index 97% rename from apps/server/src/infra/database/management/database-management.service.spec.ts rename to apps/server/src/modules/management/service/database-management.service.spec.ts index 99ce23b0836..03c8bfb6674 100644 --- a/apps/server/src/infra/database/management/database-management.service.spec.ts +++ b/apps/server/src/modules/management/service/database-management.service.spec.ts @@ -3,6 +3,7 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; import { createCollections } from '@testing/create-collections'; import { MongoMemoryDatabaseModule } from '@testing/database'; +import { TEST_ENTITIES } from '../management.entity.imports'; import { DatabaseManagementService } from './database-management.service'; const randomChars = () => new ObjectId().toHexString(); @@ -12,7 +13,7 @@ describe('DatabaseManagementService', () => { let orm: MikroORM; beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: TEST_ENTITIES })], providers: [DatabaseManagementService], }).compile(); diff --git a/apps/server/src/infra/database/management/database-management.service.ts b/apps/server/src/modules/management/service/database-management.service.ts similarity index 73% rename from apps/server/src/infra/database/management/database-management.service.ts rename to apps/server/src/modules/management/service/database-management.service.ts index 83063666aa9..4167d85ca16 100644 --- a/apps/server/src/infra/database/management/database-management.service.ts +++ b/apps/server/src/modules/management/service/database-management.service.ts @@ -14,12 +14,12 @@ export class DatabaseManagementService { return connection; } - getDatabaseCollection(collectionName: string): Collection { + public getDatabaseCollection(collectionName: string): Collection { const collection = this.db.collection(collectionName); return collection; } - async importCollection(collectionName: string, jsonDocuments: unknown[]): Promise { + public async importCollection(collectionName: string, jsonDocuments: unknown[]): Promise { if (jsonDocuments.length === 0) { return 0; } @@ -31,19 +31,19 @@ export class DatabaseManagementService { return insertedCount; } - async findDocumentsOfCollection(collectionName: string): Promise { + public async findDocumentsOfCollection(collectionName: string): Promise { const collection = this.getDatabaseCollection(collectionName); const documents = (await collection.find({}).toArray()) as unknown[]; return documents; } - async clearCollection(collectionName: string): Promise { + public async clearCollection(collectionName: string): Promise { const collection = this.getDatabaseCollection(collectionName); const { deletedCount } = await collection.deleteMany({}); return deletedCount || 0; } - async getCollectionNames(): Promise { + public async getCollectionNames(): Promise { const collections = (await this.db.listCollections(undefined, { nameOnly: true }).toArray()) as unknown[] as { name: string; }[]; @@ -51,38 +51,38 @@ export class DatabaseManagementService { return collectionNames; } - async collectionExists(collectionName: string): Promise { + public async collectionExists(collectionName: string): Promise { const collections = await this.getCollectionNames(); const result = collections.includes(collectionName); return result; } - async createCollection(collectionName: string): Promise { + public async createCollection(collectionName: string): Promise { await this.db.createCollection(collectionName); } - async dropCollection(collectionName: string): Promise { + public async dropCollection(collectionName: string): Promise { await this.db.dropCollection(collectionName); } - async syncIndexes(): Promise { + public async syncIndexes(): Promise { return this.orm.getSchemaGenerator().ensureIndexes(); } - async migrationUp(from?: string, to?: string, only?: string): Promise { + public async migrationUp(from?: string, to?: string, only?: string): Promise { const migrator = this.orm.getMigrator(); const params = this.migrationParams(only, from, to); await migrator.up(params); } - async migrationDown(from?: string, to?: string, only?: string): Promise { + public async migrationDown(from?: string, to?: string, only?: string): Promise { const migrator = this.orm.getMigrator(); const params = this.migrationParams(only, from, to); await migrator.down(params); } - async migrationPending(): Promise { + public async migrationPending(): Promise { const migrator = this.orm.getMigrator(); const pendingMigrations = await migrator.getPendingMigrations(); return pendingMigrations; diff --git a/apps/server/src/modules/management/uc/database-management.uc.spec.ts b/apps/server/src/modules/management/uc/database-management.uc.spec.ts index 363e7d3b042..592c4d0a9a4 100644 --- a/apps/server/src/modules/management/uc/database-management.uc.spec.ts +++ b/apps/server/src/modules/management/uc/database-management.uc.spec.ts @@ -1,7 +1,6 @@ import { LegacyLogger } from '@core/logger'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Configuration } from '@hpi-schul-cloud/commons/lib'; -import { DatabaseManagementService } from '@infra/database'; import { DefaultEncryptionService, LdapEncryptionService, SymmetricKeyEncryptionService } from '@infra/encryption'; import { FileSystemAdapter } from '@infra/file-system'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; @@ -13,6 +12,7 @@ import { setupEntities } from '@testing/setup-entities'; import { BsonConverter } from '../converter/bson.converter'; import { generateSeedData } from '../seed-data/generateSeedData'; import { MediaSourcesSeedDataService, SystemsSeedDataService } from '../service'; +import { DatabaseManagementService } from '../service/database-management.service'; import { DatabaseManagementUc } from './database-management.uc'; describe('DatabaseManagementService', () => { diff --git a/apps/server/src/modules/management/uc/database-management.uc.ts b/apps/server/src/modules/management/uc/database-management.uc.ts index 706b55d3d9b..ab91aebddf4 100644 --- a/apps/server/src/modules/management/uc/database-management.uc.ts +++ b/apps/server/src/modules/management/uc/database-management.uc.ts @@ -1,6 +1,5 @@ import { LegacyLogger } from '@core/logger'; import { Configuration } from '@hpi-schul-cloud/commons'; -import { DatabaseManagementService } from '@infra/database'; import { DefaultEncryptionService, EncryptionService, LdapEncryptionService } from '@infra/encryption'; import { FileSystemAdapter } from '@infra/file-system'; import { UmzugMigration } from '@mikro-orm/migrations-mongodb'; @@ -13,6 +12,7 @@ import { orderBy } from 'lodash'; import { BsonConverter } from '../converter/bson.converter'; import { generateSeedData } from '../seed-data/generateSeedData'; import { MediaSourcesSeedDataService, SystemsSeedDataService } from '../service'; +import { DatabaseManagementService } from '../service/database-management.service'; export interface CollectionFilePath { filePath: string; diff --git a/apps/server/src/modules/media-source/repo/media-source.repo.spec.ts b/apps/server/src/modules/media-source/repo/media-source.repo.spec.ts index e0385640b34..698dda0916b 100644 --- a/apps/server/src/modules/media-source/repo/media-source.repo.spec.ts +++ b/apps/server/src/modules/media-source/repo/media-source.repo.spec.ts @@ -17,7 +17,7 @@ describe(MediaSourceRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [MediaSourceEntity] })], providers: [MediaSourceRepo], }).compile(); diff --git a/apps/server/src/modules/oauth/repo/mikro-orm/oauth-session-token.repo.spec.ts b/apps/server/src/modules/oauth/repo/mikro-orm/oauth-session-token.repo.spec.ts index 0ee1655bda3..37ce2d36412 100644 --- a/apps/server/src/modules/oauth/repo/mikro-orm/oauth-session-token.repo.spec.ts +++ b/apps/server/src/modules/oauth/repo/mikro-orm/oauth-session-token.repo.spec.ts @@ -15,7 +15,7 @@ describe(OauthSessionTokenMikroOrmRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [OauthSessionTokenEntity] })], providers: [{ provide: OAUTH_SESSION_TOKEN_REPO, useClass: OauthSessionTokenMikroOrmRepo }], }).compile(); diff --git a/apps/server/src/modules/pseudonym/repo/external-tool-pseudonym.repo.integration.spec.ts b/apps/server/src/modules/pseudonym/repo/external-tool-pseudonym.repo.integration.spec.ts index a27795c69c3..0e32196ad25 100644 --- a/apps/server/src/modules/pseudonym/repo/external-tool-pseudonym.repo.integration.spec.ts +++ b/apps/server/src/modules/pseudonym/repo/external-tool-pseudonym.repo.integration.spec.ts @@ -1,12 +1,12 @@ +import { LegacyLogger } from '@core/logger'; import { createMock } from '@golevelup/ts-jest'; import { NotFoundError } from '@mikro-orm/core'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { MongoMemoryDatabaseModule } from '@testing/database'; - -import { LegacyLogger } from '@core/logger'; import { Page, Pseudonym } from '@shared/domain/domainobject'; +import { User } from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; +import { MongoMemoryDatabaseModule } from '@testing/database'; import { pseudonymFactory } from '@testing/factory/domainobject'; import { userFactory } from '@testing/factory/user.factory'; import { v4 as uuidv4 } from 'uuid'; @@ -22,7 +22,7 @@ describe('ExternalToolPseudonymRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [ExternalToolPseudonymEntity, User] })], providers: [ ExternalToolPseudonymRepo, { diff --git a/apps/server/src/modules/pseudonym/repo/pseudonyms.repo.spec.ts b/apps/server/src/modules/pseudonym/repo/pseudonyms.repo.spec.ts index eba66001536..53ec8856204 100644 --- a/apps/server/src/modules/pseudonym/repo/pseudonyms.repo.spec.ts +++ b/apps/server/src/modules/pseudonym/repo/pseudonyms.repo.spec.ts @@ -7,7 +7,6 @@ import { Pseudonym } from '@shared/domain/domainobject'; import { cleanupCollections } from '@testing/cleanup-collections'; import { MongoMemoryDatabaseModule } from '@testing/database'; import { pseudonymFactory } from '@testing/factory/domainobject'; -import { userFactory } from '@testing/factory/user.factory'; import { v4 as uuidv4 } from 'uuid'; import { PseudonymEntity } from '../entity'; import { pseudonymEntityFactory } from '../testing'; @@ -20,7 +19,7 @@ describe('PseudonymRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [PseudonymEntity] })], providers: [ PseudonymsRepo, { @@ -123,26 +122,26 @@ describe('PseudonymRepo', () => { describe('findPseudonymsByUserId', () => { describe('when pseudonym is existing', () => { const setup = async () => { - const user1 = userFactory.buildWithId(); - const user2 = userFactory.buildWithId(); - const pseudonym1: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user1.id }); - const pseudonym2: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user1.id }); - const pseudonym3: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user2.id }); - const pseudonym4: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user2.id }); + const userId1 = new ObjectId().toHexString(); + const userId2 = new ObjectId().toHexString(); + const pseudonym1: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId1 }); + const pseudonym2: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId1 }); + const pseudonym3: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId2 }); + const pseudonym4: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId2 }); await em.persistAndFlush([pseudonym1, pseudonym2, pseudonym3, pseudonym4]); return { - user1, + userId1, pseudonym1, pseudonym2, }; }; it('should return array of pseudonyms', async () => { - const { user1, pseudonym1, pseudonym2 } = await setup(); + const { userId1, pseudonym1, pseudonym2 } = await setup(); - const result: Pseudonym[] = await repo.findByUserId(user1.id); + const result: Pseudonym[] = await repo.findByUserId(userId1); const expectedArray = [ { @@ -234,29 +233,29 @@ describe('PseudonymRepo', () => { describe('deletePseudonymsByUserId', () => { describe('when pseudonyms are not existing', () => { const setup = () => { - const user = userFactory.buildWithId(); + const userId = new ObjectId().toHexString(); return { - user, + userId, }; }; it('should return empty array', async () => { - const { user } = setup(); + const { userId } = setup(); - const result = await repo.deletePseudonymsByUserId(user.id); + const result = await repo.deletePseudonymsByUserId(userId); expect(result).toEqual([]); }); }); describe('when pseudonyms are existing', () => { const setup = async () => { - const user1 = userFactory.buildWithId(); - const user2 = userFactory.buildWithId(); - const pseudonym1: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user1.id }); - const pseudonym2: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user1.id }); - const pseudonym3: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user2.id }); - const pseudonym4: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: user2.id }); + const userId1 = new ObjectId().toHexString(); + const userId2 = new ObjectId().toHexString(); + const pseudonym1: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId1 }); + const pseudonym2: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId1 }); + const pseudonym3: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId2 }); + const pseudonym4: PseudonymEntity = pseudonymEntityFactory.buildWithId({ userId: userId2 }); await em.persistAndFlush([pseudonym1, pseudonym2, pseudonym3, pseudonym4]); @@ -264,14 +263,14 @@ describe('PseudonymRepo', () => { return { expectedResult, - user1, + userId1, }; }; it('should delete all pseudonyms for userId', async () => { - const { expectedResult, user1 } = await setup(); + const { expectedResult, userId1 } = await setup(); - const result = await repo.deletePseudonymsByUserId(user1.id); + const result = await repo.deletePseudonymsByUserId(userId1); expect(result).toEqual(expectedResult); }); diff --git a/apps/server/src/modules/registration-pin/repo/registration-pin.repo.spec.ts b/apps/server/src/modules/registration-pin/repo/registration-pin.repo.spec.ts index 93e9a76e0dd..4c75b6823c4 100644 --- a/apps/server/src/modules/registration-pin/repo/registration-pin.repo.spec.ts +++ b/apps/server/src/modules/registration-pin/repo/registration-pin.repo.spec.ts @@ -1,9 +1,11 @@ import { EntityManager } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; +import { User } from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { MongoMemoryDatabaseModule } from '@testing/database'; import { userFactory } from '@testing/factory/user.factory'; import { RegistrationPinRepo } from '.'; +import { RegistrationPinEntity } from '../entity'; import { registrationPinEntityFactory } from '../entity/testing'; describe(RegistrationPinRepo.name, () => { @@ -13,7 +15,7 @@ describe(RegistrationPinRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [RegistrationPinEntity, User] })], providers: [RegistrationPinRepo], }).compile(); diff --git a/apps/server/src/modules/room-membership/repo/room-membership.repo.spec.ts b/apps/server/src/modules/room-membership/repo/room-membership.repo.spec.ts index 31bd27cac4e..512b0e74d26 100644 --- a/apps/server/src/modules/room-membership/repo/room-membership.repo.spec.ts +++ b/apps/server/src/modules/room-membership/repo/room-membership.repo.spec.ts @@ -15,7 +15,7 @@ describe('RoomMembershipRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [RoomMembershipEntity] })], providers: [RoomMembershipRepo], }).compile(); diff --git a/apps/server/src/modules/room-membership/service/room-membership.service.spec.ts b/apps/server/src/modules/room-membership/service/room-membership.service.spec.ts index 5036c5044c2..bc0008dd179 100644 --- a/apps/server/src/modules/room-membership/service/room-membership.service.spec.ts +++ b/apps/server/src/modules/room-membership/service/room-membership.service.spec.ts @@ -10,6 +10,7 @@ import { UserService } from '@modules/user'; import { BadRequestException } from '@nestjs/common/exceptions'; import { ConfigService } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; +import { User } from '@shared/domain/entity'; import { RoleName } from '@shared/domain/interface'; import { MongoMemoryDatabaseModule } from '@testing/database'; import { roleFactory } from '@testing/factory/role.factory'; @@ -33,7 +34,7 @@ describe('RoomMembershipService', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [User] })], providers: [ RoomMembershipService, { diff --git a/apps/server/src/modules/room/repo/room.repo.spec.ts b/apps/server/src/modules/room/repo/room.repo.spec.ts index a187b0e31cb..abbfc39b2d9 100644 --- a/apps/server/src/modules/room/repo/room.repo.spec.ts +++ b/apps/server/src/modules/room/repo/room.repo.spec.ts @@ -17,7 +17,7 @@ describe('RoomRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [RoomEntity] })], providers: [RoomRepo], }).compile(); diff --git a/apps/server/src/modules/roster/service/feathers-roster.service.ts b/apps/server/src/modules/roster/service/feathers-roster.service.ts index 2c60634f3e2..9fad37fa74b 100644 --- a/apps/server/src/modules/roster/service/feathers-roster.service.ts +++ b/apps/server/src/modules/roster/service/feathers-roster.service.ts @@ -56,7 +56,7 @@ interface Group { * This service will be called from feathers to get the roster data for ctl pseudonyms {@link ExternalToolPseudonymEntity}. * These data will be used e.g. by bettermarks to resolve and display the usernames. */ -@Injectable() +@Injectable() // Why Feathers in name? export class FeathersRosterService { constructor( private readonly userService: UserService, diff --git a/apps/server/src/modules/school-license/repo/mikro-orm/media-school-license.repo.integration.spec.ts b/apps/server/src/modules/school-license/repo/mikro-orm/media-school-license.repo.integration.spec.ts index b8f61215024..c432a1c8f38 100644 --- a/apps/server/src/modules/school-license/repo/mikro-orm/media-school-license.repo.integration.spec.ts +++ b/apps/server/src/modules/school-license/repo/mikro-orm/media-school-license.repo.integration.spec.ts @@ -2,11 +2,12 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { MediaSourceEntity } from '@modules/media-source/entity'; import { mediaSourceEntityFactory } from '@modules/media-source/testing'; import { Test, TestingModule } from '@nestjs/testing'; +import { SchoolEntity } from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { MongoMemoryDatabaseModule } from '@testing/database'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { MediaSchoolLicense } from '../../domain'; -import { MediaSchoolLicenseEntity } from '../../entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '../../entity'; import { mediaSchoolLicenseEntityFactory, mediaSchoolLicenseFactory } from '../../testing'; import { MediaSchoolLicenseMikroOrmRepo } from './media-school-license.repo'; @@ -17,7 +18,11 @@ describe(MediaSchoolLicenseMikroOrmRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [MediaSchoolLicenseEntity, MediaSourceEntity, SchoolLicenseEntity, SchoolEntity], + }), + ], providers: [MediaSchoolLicenseMikroOrmRepo], }).compile(); diff --git a/apps/server/src/modules/school/repo/mikro-orm/mapper/county.embeddable.mapper.ts b/apps/server/src/modules/school/repo/mikro-orm/mapper/county.embeddable.mapper.ts index a3e1847b834..fd394b581e2 100644 --- a/apps/server/src/modules/school/repo/mikro-orm/mapper/county.embeddable.mapper.ts +++ b/apps/server/src/modules/school/repo/mikro-orm/mapper/county.embeddable.mapper.ts @@ -3,7 +3,7 @@ import { CountyEmbeddable } from '@shared/domain/entity'; import { County } from '../../../domain'; export class CountyEmbeddableMapper { - static mapToEntity(county: County): CountyEmbeddable { + public static mapToEntity(county: County): CountyEmbeddable { const countyProps = county.getProps(); const countyEmbeddable = new CountyEmbeddable({ ...countyProps, _id: new ObjectId(countyProps.id) }); @@ -11,7 +11,7 @@ export class CountyEmbeddableMapper { return countyEmbeddable; } - static mapToDo(embeddable: CountyEmbeddable): County { + public static mapToDo(embeddable: CountyEmbeddable): County { const county = new County({ id: embeddable._id.toHexString(), name: embeddable.name, diff --git a/apps/server/src/modules/school/repo/mikro-orm/school-year.repo.integration.spec.ts b/apps/server/src/modules/school/repo/mikro-orm/school-year.repo.integration.spec.ts index 9df3bdd02c7..58d35286ee3 100644 --- a/apps/server/src/modules/school/repo/mikro-orm/school-year.repo.integration.spec.ts +++ b/apps/server/src/modules/school/repo/mikro-orm/school-year.repo.integration.spec.ts @@ -15,7 +15,7 @@ describe('SchoolYearMikroOrmRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [SchoolYearEntity] })], providers: [{ provide: SCHOOL_YEAR_REPO, useClass: SchoolYearMikroOrmRepo }], }).compile(); diff --git a/apps/server/src/modules/school/repo/mikro-orm/school.repo.integration.spec.ts b/apps/server/src/modules/school/repo/mikro-orm/school.repo.integration.spec.ts index 9c6a0883719..4e983ad89f0 100644 --- a/apps/server/src/modules/school/repo/mikro-orm/school.repo.integration.spec.ts +++ b/apps/server/src/modules/school/repo/mikro-orm/school.repo.integration.spec.ts @@ -25,7 +25,7 @@ describe('SchoolMikroOrmRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [SchoolEntity] })], providers: [{ provide: SCHOOL_REPO, useClass: SchoolMikroOrmRepo }], }).compile(); diff --git a/apps/server/src/modules/server-console/api-test/server-console.api.spec.ts b/apps/server/src/modules/server-console/api-test/server-console.api.spec.ts deleted file mode 100644 index 9becd3571a1..00000000000 --- a/apps/server/src/modules/server-console/api-test/server-console.api.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { INestApplicationContext } from '@nestjs/common'; - -import { ConsoleWriterService } from '@infra/console'; -import { ServerConsoleModule } from '@modules/server-console/server-console.app.module'; -import { BootstrapConsole, ConsoleService } from 'nestjs-console'; -import { execute, TestBootstrapConsole } from './test-bootstrap.console'; - -describe('ServerConsole (API)', () => { - let app: INestApplicationContext; - let bootstrap: BootstrapConsole; - let consoleService: ConsoleService; - let consoleWriter: ConsoleWriterService; - let logMock: jest.SpyInstance; - beforeEach(async () => { - bootstrap = new TestBootstrapConsole({ - module: ServerConsoleModule, - useDecorators: true, - }); - app = await bootstrap.init(); - await app.init(); - consoleService = app.get(ConsoleService); - consoleWriter = app.get(ConsoleWriterService); - logMock = jest.spyOn(consoleWriter, 'info').mockImplementation(); - }); - - afterEach(async () => { - await app.close(); - logMock.mockReset(); - consoleService.resetCli(); - }); - - it('should produce default output when executing "console server test"', async () => { - await execute(bootstrap, ['server', 'test']); - expect(logMock).toHaveBeenCalledWith('Schulcloud Server API'); - }); - it('should return input string param when executing "console server out "', async () => { - const sampleInputString = 'sample-input'; - await execute(bootstrap, ['server', 'out', sampleInputString]); - expect(logMock).toHaveBeenCalledWith(`input was: ${sampleInputString}`); - }); -}); diff --git a/apps/server/src/modules/server-console/server.console.spec.ts b/apps/server/src/modules/server-console/server.console.spec.ts deleted file mode 100644 index b9ba740719c..00000000000 --- a/apps/server/src/modules/server-console/server.console.spec.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ConsoleWriterService } from '@infra/console'; -import { Test, TestingModule } from '@nestjs/testing'; -import { ServerConsoleModule } from './server-console.app.module'; -import { ServerConsole } from './server.console'; - -describe('ServerConsole', () => { - let serverConsole: ServerConsole; - let consoleWriter: ConsoleWriterService; - let module: TestingModule; - beforeAll(async () => { - module = await Test.createTestingModule({ - imports: [ServerConsoleModule], - }).compile(); - - serverConsole = module.get(ServerConsole); - consoleWriter = module.get(ConsoleWriterService); - }); - afterAll(async () => { - await module.close(); - }); - - describe('root', () => { - it('should spin "Schulcloud Server API"', () => { - const consoleInfoSpy = jest.spyOn(consoleWriter, 'info'); - serverConsole.getHello(); - expect(consoleInfoSpy).toHaveBeenCalledWith('Schulcloud Server API'); - consoleInfoSpy.mockReset(); - }); - it('should spin input as output', () => { - const consoleInfoSpy = jest.spyOn(consoleWriter, 'info'); - serverConsole.getInOut('sample'); - expect(consoleInfoSpy).toHaveBeenCalledWith('input was: sample'); - consoleInfoSpy.mockReset(); - }); - }); -}); diff --git a/apps/server/src/modules/server-console/server.console.ts b/apps/server/src/modules/server-console/server.console.ts deleted file mode 100644 index 32ff8182241..00000000000 --- a/apps/server/src/modules/server-console/server.console.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* eslint-disable no-console */ -import { Command, Console } from 'nestjs-console'; -import { ConsoleWriterService } from '@infra/console'; - -@Console({ command: 'server', description: 'sample server console' }) -export class ServerConsole { - constructor(private consoleWriter: ConsoleWriterService) {} - - /** test method for console output */ - @Command({ command: 'test', description: 'sample test output' }) - getHello(): void { - this.consoleWriter.info('Schulcloud Server API'); - } - - /** test method for console input */ - @Command({ command: 'out ', description: 'return input args' }) - getInOut(whatever: string): void { - this.consoleWriter.info(`input was: ${whatever}`); - } -} diff --git a/apps/server/src/modules/server/admin-api-server.entity.imports.ts b/apps/server/src/modules/server/admin-api-server.entity.imports.ts new file mode 100644 index 00000000000..16aef4ef8dd --- /dev/null +++ b/apps/server/src/modules/server/admin-api-server.entity.imports.ts @@ -0,0 +1,62 @@ +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity, DeletionRequestEntity } from '@modules/deletion/repo/entity'; +import { FileEntity } from '@modules/files/entity'; +import { GroupEntity } from '@modules/group/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { + Course, + CourseGroup, + CourseNews, + FederalStateEntity, + News, + Role, + SchoolEntity, + SchoolNews, + SchoolYearEntity, + StorageProviderEntity, + TeamEntity, + TeamNews, + User, +} from '@shared/domain/entity'; + +export const ENTITIES = [ + AccountEntity, + Role, + DeletionRequestEntity, + DeletionLogEntity, + SchoolEntity, + SchoolYearEntity, + StorageProviderEntity, + FederalStateEntity, + User, + Course, + CourseGroup, + ClassEntity, + GroupEntity, + ExternalToolEntity, + ContextExternalToolEntity, + SchoolExternalToolEntity, + FileEntity, + CourseNews, + News, + SchoolNews, + TeamNews, + TeamEntity, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + RegistrationPinEntity, + LtiDeepLinkTokenEntity, + BoardNodeEntity, + RoomMembershipEntity, +]; + +export const TEST_ENTITIES = [...ENTITIES]; diff --git a/apps/server/src/modules/server/admin-api.server.app.module.ts b/apps/server/src/modules/server/admin-api.server.app.module.ts index 27c2c17e29e..faf51234d4f 100644 --- a/apps/server/src/modules/server/admin-api.server.app.module.ts +++ b/apps/server/src/modules/server/admin-api.server.app.module.ts @@ -6,19 +6,18 @@ import { EtherpadClientModule } from '@infra/etherpad-client'; import { RabbitMQWrapperModule, RabbitMQWrapperTestModule } from '@infra/rabbitmq'; import { MikroOrmModule } from '@mikro-orm/nestjs'; import { DeletionApiModule } from '@modules/deletion/deletion-api.module'; -import { FileEntity } from '@modules/files/entity'; import { LegacySchoolAdminApiModule } from '@modules/legacy-school/legacy-school-admin.api-module'; +import { AdminApiRegistrationPinModule } from '@modules/registration-pin/admin-api-registration-pin.module'; import { ToolAdminApiModule } from '@modules/tool/tool-admin-api.module'; import { UserAdminApiModule } from '@modules/user/user-admin-api.module'; -import { DynamicModule, Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { CqrsModule } from '@nestjs/cqrs'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; -import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@testing/database'; -import { AdminApiRegistrationPinModule } from '../registration-pin/admin-api-registration-pin.module'; +import { MongoMemoryDatabaseModule } from '@testing/database'; import { adminApiServerConfig } from './admin-api-server.config'; +import { ENTITIES, TEST_ENTITIES } from './admin-api-server.entity.imports'; const serverModules = [ ConfigModule.forRoot(createConfigModuleOptions(adminApiServerConfig)), @@ -44,7 +43,7 @@ const serverModules = [ clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: [...ALL_ENTITIES, FileEntity], + entities: ENTITIES, // debug: true, // use it for locally debugging of queries }), CqrsModule, @@ -56,20 +55,9 @@ export class AdminApiServerModule {} @Module({ imports: [ ...serverModules, - MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions }), + MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), RabbitMQWrapperTestModule, LoggerModule, ], }) -export class AdminApiServerTestModule { - static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { - return { - module: AdminApiServerTestModule, - imports: [ - ...serverModules, - MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, ...options }), - RabbitMQWrapperTestModule, - ], - }; - } -} +export class AdminApiServerTestModule {} diff --git a/apps/server/src/modules/server/server.app.module.ts b/apps/server/src/modules/server/server.app.module.ts index a4544840cec..1cf51618c3b 100644 --- a/apps/server/src/modules/server/server.app.module.ts +++ b/apps/server/src/modules/server/server.app.module.ts @@ -43,15 +43,15 @@ import { UserLoginMigrationApiModule } from '@modules/user-login-migration/user- import { UsersAdminApiModule } from '@modules/user/legacy/users-admin-api.module'; import { UserApiModule } from '@modules/user/user-api.module'; import { VideoConferenceApiModule } from '@modules/video-conference/video-conference-api.module'; -import { DynamicModule, Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { createConfigModuleOptions } from '@shared/common/config-module-options'; import { defaultMikroOrmOptions } from '@shared/common/defaultMikroOrmOptions'; -import { ALL_ENTITIES } from '@shared/domain/entity'; -import { MongoDatabaseModuleOptions, MongoMemoryDatabaseModule } from '@testing/database'; +import { MongoMemoryDatabaseModule } from '@testing/database'; import { SchoolLicenseApiModule } from '../school-license/school-license-api.module'; import { ServerConfigController, ServerController, ServerUc } from './api'; import { SERVER_CONFIG_TOKEN, serverConfig } from './server.config'; +import { ENTITIES, TEST_ENTITIES } from './server.entity.imports'; const serverModules = [ ConfigModule.forRoot(createConfigModuleOptions(serverConfig)), @@ -126,7 +126,7 @@ const controllers = [ServerController, ServerConfigController]; clientUrl: DB_URL, password: DB_PASSWORD, user: DB_USERNAME, - entities: ALL_ENTITIES, + entities: ENTITIES, // debug: true, // use it for locally debugging of queries }), @@ -148,24 +148,11 @@ export class ServerModule {} @Module({ imports: [ ...serverModules, - MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions }), + MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, entities: TEST_ENTITIES }), RabbitMQWrapperTestModule, LoggerModule, ], providers, controllers, }) -export class ServerTestModule { - static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { - return { - module: ServerTestModule, - imports: [ - ...serverModules, - MongoMemoryDatabaseModule.forRoot({ ...defaultMikroOrmOptions, ...options }), - RabbitMQWrapperTestModule, - ], - providers, - controllers, - }; - } -} +export class ServerTestModule {} diff --git a/apps/server/src/shared/domain/entity/all-entities.ts b/apps/server/src/modules/server/server.entity.imports.ts similarity index 67% rename from apps/server/src/shared/domain/entity/all-entities.ts rename to apps/server/src/modules/server/server.entity.imports.ts index 031728adacc..878ad0bd556 100644 --- a/apps/server/src/shared/domain/entity/all-entities.ts +++ b/apps/server/src/modules/server/server.entity.imports.ts @@ -21,34 +21,34 @@ import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { ImportUser } from '@modules/user-import/entity'; import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; -import { ColumnBoardNode } from './column-board-node.entity'; -import { Course } from './course.entity'; -import { CourseGroup } from './coursegroup.entity'; -import { DashboardGridElementModel, DashboardModelEntity } from './dashboard.model.entity'; -import { CountyEmbeddable, FederalStateEntity } from './federal-state.entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; import { ColumnboardBoardElement, LegacyBoard, LegacyBoardElement, LessonBoardElement, TaskBoardElement, -} from './legacy-board'; -import { LessonEntity } from './lesson.entity'; -import { LtiTool } from './ltitool.entity'; -import { Material } from './materials.entity'; -import { CourseNews, News, SchoolNews, TeamNews } from './news.entity'; -import { Role } from './role.entity'; -import { SchoolEntity, SchoolRolePermission, SchoolRoles } from './school.entity'; -import { SchoolYearEntity } from './schoolyear.entity'; -import { StorageProviderEntity } from './storageprovider.entity'; -import { Submission } from './submission.entity'; -import { Task } from './task.entity'; -import { TeamEntity, TeamUserEntity } from './team.entity'; -import { UserLoginMigrationEntity } from './user-login-migration.entity'; -import { User } from './user.entity'; -import { VideoConference } from './video-conference.entity'; +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; -export const ALL_ENTITIES = [ +export const ENTITIES = [ AccountEntity, LegacyBoard, LegacyBoardElement, @@ -109,3 +109,5 @@ export const ALL_ENTITIES = [ OauthSessionTokenEntity, LtiDeepLinkTokenEntity, ]; + +export const TEST_ENTITIES = [...ENTITIES]; diff --git a/apps/server/src/modules/sharing/repo/share-token.repo.integration.spec.ts b/apps/server/src/modules/sharing/repo/share-token.repo.integration.spec.ts index 62c2ad89853..5442b2066a4 100644 --- a/apps/server/src/modules/sharing/repo/share-token.repo.integration.spec.ts +++ b/apps/server/src/modules/sharing/repo/share-token.repo.integration.spec.ts @@ -2,10 +2,12 @@ import { LegacyLogger } from '@core/logger'; import { createMock } from '@golevelup/ts-jest'; import { EntityManager } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; +import { SchoolEntity } from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { MongoMemoryDatabaseModule } from '@testing/database'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; import { ShareTokenContextType } from '../domainobject/share-token.do'; +import { ShareToken } from '../entity/share-token.entity'; import { shareTokenDOFactory } from '../testing/share-token.do.factory'; import { ShareTokenRepo } from './share-token.repo'; @@ -16,7 +18,7 @@ describe('ShareTokenRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [ShareToken, SchoolEntity] })], providers: [ ShareTokenRepo, { diff --git a/apps/server/src/modules/system/repo/mikro-orm/system.repo.spec.ts b/apps/server/src/modules/system/repo/mikro-orm/system.repo.spec.ts index 81f5761da2e..84b49ce1c39 100644 --- a/apps/server/src/modules/system/repo/mikro-orm/system.repo.spec.ts +++ b/apps/server/src/modules/system/repo/mikro-orm/system.repo.spec.ts @@ -23,7 +23,7 @@ describe(SystemMikroOrmRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [SystemEntity] })], providers: [{ provide: SYSTEM_REPO, useClass: SystemMikroOrmRepo }], }).compile(); diff --git a/apps/server/src/modules/tool/context-external-tool/repo/mikro-orm/lti-deep-link-token.repo.spec.ts b/apps/server/src/modules/tool/context-external-tool/repo/mikro-orm/lti-deep-link-token.repo.spec.ts index 1ae57a80519..1854b9f3919 100644 --- a/apps/server/src/modules/tool/context-external-tool/repo/mikro-orm/lti-deep-link-token.repo.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/repo/mikro-orm/lti-deep-link-token.repo.spec.ts @@ -16,7 +16,7 @@ describe(LtiDeepLinkTokenMikroOrmRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [LtiDeepLinkTokenEntity] })], providers: [{ provide: LTI_DEEP_LINK_TOKEN_REPO, useClass: LtiDeepLinkTokenMikroOrmRepo }], }).compile(); diff --git a/apps/server/src/modules/user-import/repo/import-user.repo.spec.ts b/apps/server/src/modules/user-import/repo/import-user.repo.spec.ts index e23f33d5919..7920cf67e4e 100644 --- a/apps/server/src/modules/user-import/repo/import-user.repo.spec.ts +++ b/apps/server/src/modules/user-import/repo/import-user.repo.spec.ts @@ -28,7 +28,7 @@ describe('ImportUserRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [ImportUser, SchoolEntity, User] })], providers: [ImportUserRepo], }).compile(); repo = module.get(ImportUserRepo); diff --git a/apps/server/src/modules/user-import/service/schulconnex-fetch-import-users.service.spec.ts b/apps/server/src/modules/user-import/service/schulconnex-fetch-import-users.service.spec.ts index a34f3128b14..a337d898d78 100644 --- a/apps/server/src/modules/user-import/service/schulconnex-fetch-import-users.service.spec.ts +++ b/apps/server/src/modules/user-import/service/schulconnex-fetch-import-users.service.spec.ts @@ -7,7 +7,7 @@ import { systemEntityFactory, systemFactory } from '@modules/system/testing'; import { UserService } from '@modules/user'; import { Test, TestingModule } from '@nestjs/testing'; import { UserDO } from '@shared/domain/domainobject'; -import { SchoolEntity } from '@shared/domain/entity'; +import { SchoolEntity, User } from '@shared/domain/entity'; import { RoleName } from '@shared/domain/interface'; import { MongoMemoryDatabaseModule } from '@testing/database'; import { schoolEntityFactory } from '@testing/factory/school-entity.factory'; @@ -29,7 +29,7 @@ describe(SchulconnexFetchImportUsersService.name, () => { await setupEntities(); module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [User] })], providers: [ SchulconnexFetchImportUsersService, { diff --git a/apps/server/src/modules/user-import/service/user-import.service.spec.ts b/apps/server/src/modules/user-import/service/user-import.service.spec.ts index 94f87ed7b4d..e7494b0f6fc 100644 --- a/apps/server/src/modules/user-import/service/user-import.service.spec.ts +++ b/apps/server/src/modules/user-import/service/user-import.service.spec.ts @@ -40,7 +40,7 @@ describe(UserImportService.name, () => { await setupEntities(); module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [User] })], providers: [ UserImportService, { diff --git a/apps/server/src/modules/user-license/repo/media-user-license.repo.spec.ts b/apps/server/src/modules/user-license/repo/media-user-license.repo.spec.ts index c3a323e45d3..47dc86e2626 100644 --- a/apps/server/src/modules/user-license/repo/media-user-license.repo.spec.ts +++ b/apps/server/src/modules/user-license/repo/media-user-license.repo.spec.ts @@ -9,12 +9,12 @@ import { mediaSourceVidisConfigEmbeddableFactory, } from '@modules/media-source/testing'; import { Test, TestingModule } from '@nestjs/testing'; -import { User as UserEntity } from '@shared/domain/entity'; +import { User } from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { MongoMemoryDatabaseModule } from '@testing/database'; import { userFactory } from '@testing/factory/user.factory'; import { MediaUserLicense } from '../domain'; -import { MediaUserLicenseEntity } from '../entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '../entity'; import { mediaUserLicenseEntityFactory, mediaUserLicenseFactory } from '../testing'; import { MediaUserLicenseRepo } from './media-user-license.repo'; @@ -25,7 +25,11 @@ describe(MediaUserLicenseRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [MediaUserLicenseEntity, UserLicenseEntity, MediaSourceEntity, User], + }), + ], providers: [MediaUserLicenseRepo], }).compile(); @@ -44,10 +48,10 @@ describe(MediaUserLicenseRepo.name, () => { describe('findMediaUserLicensesForUser', () => { describe('when searching for a users media licences', () => { const setup = async () => { - const user: UserEntity = userFactory.build(); + const user = userFactory.build(); const vidisConfig = mediaSourceVidisConfigEmbeddableFactory.build(); const oauthConfig = mediaSourceOAuthConfigEmbeddableFactory.build(); - const mediaSource: MediaSourceEntity = mediaSourceEntityFactory.build({ + const mediaSource = mediaSourceEntityFactory.build({ vidisConfig, oauthConfig, }); diff --git a/apps/server/src/modules/user/legacy/repo/users-admin.repo.spec.ts b/apps/server/src/modules/user/legacy/repo/users-admin.repo.spec.ts index 56ba756a6d3..037bb433f2d 100644 --- a/apps/server/src/modules/user/legacy/repo/users-admin.repo.spec.ts +++ b/apps/server/src/modules/user/legacy/repo/users-admin.repo.spec.ts @@ -2,6 +2,7 @@ import { EntityManager } from '@mikro-orm/core'; import { ObjectId } from '@mikro-orm/mongodb'; import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { accountFactory } from '@modules/account/testing'; +import { ClassEntity } from '@modules/class/entity'; import { Test, TestingModule } from '@nestjs/testing'; import { Role, SchoolEntity, SchoolYearEntity, User } from '@shared/domain/entity'; import { Permission, RoleName } from '@shared/domain/interface'; @@ -114,7 +115,11 @@ describe('users admin repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [User, Role, SchoolEntity, SchoolYearEntity, AccountEntity, ClassEntity], + }), + ], providers: [UsersAdminRepo], }).compile(); repo = module.get(UsersAdminRepo); diff --git a/apps/server/src/shared/domain/entity/all-entities.spec.ts b/apps/server/src/shared/domain/entity/all-entities.spec.ts deleted file mode 100644 index 52537492386..00000000000 --- a/apps/server/src/shared/domain/entity/all-entities.spec.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { MikroORM } from '@mikro-orm/core'; -import { EntityManager } from '@mikro-orm/mongodb'; -import { Test, TestingModule } from '@nestjs/testing'; -import { createCollections } from '@testing/create-collections'; -import { MongoMemoryDatabaseModule } from '@testing/database'; -import { ALL_ENTITIES } from '.'; - -describe('BaseRepo', () => { - let orm: MikroORM; - let em: EntityManager; - let module: TestingModule; - - beforeAll(async () => { - module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot({ entities: ALL_ENTITIES })], - providers: [], - }).compile(); - - em = module.get(EntityManager); - orm = module.get(MikroORM); - - await createCollections(em); - }); - - afterAll(async () => { - await module.close(); - }); - - describe('defined', () => { - it('entity manager should be defined', () => { - expect(em).toBeDefined(); - }); - }); - - describe('When entities have index definitions', () => { - it('should ensure indexes', async () => { - await orm.getSchemaGenerator().ensureIndexes(); - }); - }); -}); diff --git a/apps/server/src/shared/domain/entity/course.entity.ts b/apps/server/src/shared/domain/entity/course.entity.ts index b244fdbf601..478379c8cd3 100644 --- a/apps/server/src/shared/domain/entity/course.entity.ts +++ b/apps/server/src/shared/domain/entity/course.entity.ts @@ -1,9 +1,9 @@ import { Collection, Entity, Enum, Index, ManyToMany, ManyToOne, OneToMany, Property, Unique } from '@mikro-orm/core'; import { ClassEntity } from '@modules/class/entity/class.entity'; import { GroupEntity } from '@modules/group/entity/group.entity'; - import { InternalServerErrorException } from '@nestjs/common/exceptions/internal-server-error.exception'; -import { EntityWithSchool, Learnroom } from '@shared/domain/interface'; +import { Learnroom } from '../interface/learnroom'; +import { EntityWithSchool } from '../interface/entity'; import { EntityId, LearnroomMetadata, LearnroomTypes } from '../types'; import { BaseEntityWithTimestamps } from './base.entity'; import { CourseGroup } from './coursegroup.entity'; @@ -214,7 +214,7 @@ export class Course extends BaseEntityWithTimestamps implements Learnroom, Entit return courseGroups; } - getShortTitle(): string { + public getShortTitle(): string { if (this.name.length === 1) { return this.name; } diff --git a/apps/server/src/shared/domain/entity/dashboard.entity.spec.ts b/apps/server/src/shared/domain/entity/dashboard.spec.ts similarity index 99% rename from apps/server/src/shared/domain/entity/dashboard.entity.spec.ts rename to apps/server/src/shared/domain/entity/dashboard.spec.ts index 7ce0ce0e2c2..66eee36ea31 100644 --- a/apps/server/src/shared/domain/entity/dashboard.entity.spec.ts +++ b/apps/server/src/shared/domain/entity/dashboard.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException, NotFoundException } from '@nestjs/common'; import { LearnroomTypes } from '@shared/domain/types'; import { Learnroom } from '../interface'; -import { DashboardEntity, GridElement } from './dashboard.entity'; +import { DashboardEntity, GridElement } from './dashboard'; const getLearnroomMock = (id: string): Learnroom => { return { diff --git a/apps/server/src/shared/domain/entity/dashboard.entity.ts b/apps/server/src/shared/domain/entity/dashboard.ts similarity index 84% rename from apps/server/src/shared/domain/entity/dashboard.entity.ts rename to apps/server/src/shared/domain/entity/dashboard.ts index 0664788e300..9dceefc8900 100644 --- a/apps/server/src/shared/domain/entity/dashboard.entity.ts +++ b/apps/server/src/shared/domain/entity/dashboard.ts @@ -1,6 +1,6 @@ import { BadRequestException, NotFoundException } from '@nestjs/common'; -import { Learnroom } from '@shared/domain/interface'; -import { EntityId, LearnroomMetadata } from '@shared/domain/types'; +import { Learnroom } from '../interface'; +import { EntityId, LearnroomMetadata } from '../types'; const defaultColumns = 4; @@ -40,7 +40,7 @@ export class GridElement implements IGridElement { title?: string; - private sortReferences = (a: Learnroom, b: Learnroom) => { + private sortReferences = (a: Learnroom, b: Learnroom): 1 | -1 | 0 => { const titleA = a.getMetadata().title; const titleB = b.getMetadata().title; if (titleA < titleB) { @@ -58,37 +58,37 @@ export class GridElement implements IGridElement { this.references = props.references.sort(this.sortReferences); } - static FromPersistedReference(id: EntityId, reference: Learnroom): GridElement { + public static FromPersistedReference(id: EntityId, reference: Learnroom): GridElement { return new GridElement({ id, references: [reference] }); } - static FromPersistedGroup(id: EntityId, title: string | undefined, group: Learnroom[]): GridElement { + public static FromPersistedGroup(id: EntityId, title: string | undefined, group: Learnroom[]): GridElement { return new GridElement({ id, title, references: group }); } - static FromSingleReference(reference: Learnroom): GridElement { + public static FromSingleReference(reference: Learnroom): GridElement { return new GridElement({ references: [reference] }); } - static FromGroup(title: string, references: Learnroom[]): GridElement { + public static FromGroup(title: string, references: Learnroom[]): GridElement { return new GridElement({ title, references }); } references: Learnroom[]; - hasId(): boolean { + public hasId(): boolean { return !!this.id; } - getId(): EntityId | undefined { + public getId(): EntityId | undefined { return this.id; } - getReferences(): Learnroom[] { + public getReferences(): Learnroom[] { return this.references; } - removeReferenceByIndex(index: number): void { + public removeReferenceByIndex(index: number): void { if (!this.isGroup()) { throw new BadRequestException('this element is not a group.'); } @@ -98,7 +98,7 @@ export class GridElement implements IGridElement { this.references.splice(index, 1); } - removeReference(reference: Learnroom): void { + public removeReference(reference: Learnroom): void { const index = this.references.indexOf(reference); if (index === -1) { throw new BadRequestException('reference not found.'); @@ -106,7 +106,7 @@ export class GridElement implements IGridElement { this.references.splice(index, 1); } - addReferences(anotherReference: Learnroom[]): void { + public addReferences(anotherReference: Learnroom[]): void { if (!this.isGroup()) { this.references = this.references.concat(anotherReference).sort(this.sortReferences); this.setGroupName(''); @@ -115,7 +115,7 @@ export class GridElement implements IGridElement { } } - getContent(): GridElementContent { + public getContent(): GridElementContent { if (!this.isGroup()) { const { id: referencedId, ...data } = this.references[0].getMetadata(); const metadata = { @@ -137,11 +137,11 @@ export class GridElement implements IGridElement { return groupMetadata; } - isGroup(): boolean { + public isGroup(): boolean { return this.references.length > 1; } - setGroupName(newGroupName: string): void { + public setGroupName(newGroupName: string): void { if (!this.isGroup()) { return; } @@ -159,6 +159,7 @@ export type GridElementWithPosition = { export type DashboardProps = { colums?: number; grid: GridElementWithPosition[]; userId: EntityId }; +// is not marked as Entity and should not named as Entity export class DashboardEntity { id: EntityId; @@ -192,15 +193,15 @@ export class DashboardEntity { Object.assign(this, {}); } - getId(): string { + public getId(): string { return this.id; } - getUserId(): EntityId { + public getUserId(): EntityId { return this.userId; } - getGrid(): GridElementWithPosition[] { + public getGrid(): GridElementWithPosition[] { const result = [...this.grid.keys()].map((key) => { const position = this.positionFromGridIndex(key); const value = this.grid.get(key) as IGridElement; @@ -212,7 +213,7 @@ export class DashboardEntity { return result; } - getElement(position: GridPosition): IGridElement { + public getElement(position: GridPosition): IGridElement { const element = this.grid.get(this.gridIndexFromPosition(position)); if (!element) { throw new NotFoundException('no element at grid position'); @@ -220,7 +221,7 @@ export class DashboardEntity { return element; } - moveElement(from: GridPositionWithGroupIndex, to: GridPositionWithGroupIndex): GridElementWithPosition { + public moveElement(from: GridPositionWithGroupIndex, to: GridPositionWithGroupIndex): GridElementWithPosition { const elementToMove = this.getReferencesFromPosition(from); const resultElement = this.mergeElementIntoPosition(elementToMove, to); this.removeFromPosition(from); @@ -230,7 +231,7 @@ export class DashboardEntity { }; } - setLearnRooms(rooms: Learnroom[]): void { + public setLearnRooms(rooms: Learnroom[]): void { this.removeRoomsNotInList(rooms); const newRooms = this.determineNewRoomsIn(rooms); diff --git a/apps/server/src/shared/domain/entity/dashboardElement.entity.spec.ts b/apps/server/src/shared/domain/entity/dashboardElement.entity.spec.ts index 8f9a93b6e29..5edcfbc9579 100644 --- a/apps/server/src/shared/domain/entity/dashboardElement.entity.spec.ts +++ b/apps/server/src/shared/domain/entity/dashboardElement.entity.spec.ts @@ -1,7 +1,7 @@ import { BadRequestException } from '@nestjs/common'; import { LearnroomMetadata, LearnroomTypes } from '@shared/domain/types'; -import { GridElement } from './dashboard.entity'; +import { GridElement } from './dashboard'; const learnroomMock = (id: string, name: string) => { return { diff --git a/apps/server/src/shared/domain/entity/index.ts b/apps/server/src/shared/domain/entity/index.ts index 0925cb5d2d9..af66b46570e 100644 --- a/apps/server/src/shared/domain/entity/index.ts +++ b/apps/server/src/shared/domain/entity/index.ts @@ -1,9 +1,8 @@ -export * from './all-entities'; export * from './base.entity'; export * from './column-board-node.entity'; export * from './course.entity'; export * from './coursegroup.entity'; -export * from './dashboard.entity'; +export * from './dashboard'; export * from './dashboard.model.entity'; export * from './federal-state.entity'; export * from './legacy-board'; diff --git a/apps/server/src/shared/domain/index.ts b/apps/server/src/shared/domain/index.ts index 6ace271ede5..063d6161641 100644 --- a/apps/server/src/shared/domain/index.ts +++ b/apps/server/src/shared/domain/index.ts @@ -1,3 +1,5 @@ -/** ************************************************* - * Do not re-export here! File should be empty! * - ************************************************** */ +/** ********************************************************* + * Import external dependencies directly, not from here. * + * Avoid loading all external references for testing. * + * Do not re-export here. * + ************************************************************ */ diff --git a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.spec.ts b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.spec.ts index f3511493518..36a7461e333 100644 --- a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.spec.ts +++ b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.spec.ts @@ -11,10 +11,11 @@ import { ltiDeepLinkFactory, } from '@modules/tool/context-external-tool/testing'; import { ContextExternalToolQuery } from '@modules/tool/context-external-tool/uc/dto/context-external-tool.types'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { schoolExternalToolEntityFactory } from '@modules/tool/school-external-tool/testing'; import { Test, TestingModule } from '@nestjs/testing'; -import { SchoolEntity } from '@shared/domain/entity'; +import { Course, CourseGroup, SchoolEntity, User } from '@shared/domain/entity'; import { ExternalToolRepoMapper } from '@shared/repo/externaltool/external-tool.repo.mapper'; import { cleanupCollections } from '@testing/cleanup-collections'; import { MongoMemoryDatabaseModule } from '@testing/database'; @@ -28,7 +29,19 @@ describe(ContextExternalToolRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [ + ContextExternalToolEntity, + SchoolExternalToolEntity, + SchoolEntity, + ExternalToolEntity, + User, + Course, + CourseGroup, + ], + }), + ], providers: [ ContextExternalToolRepo, ExternalToolRepoMapper, diff --git a/apps/server/src/shared/repo/course/course.repo.integration.spec.ts b/apps/server/src/shared/repo/course/course.repo.integration.spec.ts index 13017360015..d327aad88a2 100644 --- a/apps/server/src/shared/repo/course/course.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/course/course.repo.integration.spec.ts @@ -1,7 +1,7 @@ import { NotFoundError } from '@mikro-orm/core'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { Course } from '@shared/domain/entity'; +import { Course, CourseGroup } from '@shared/domain/entity'; import { SortOrder } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; import { cleanupCollections } from '@testing/cleanup-collections'; @@ -24,7 +24,7 @@ describe('course repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [Course, CourseGroup] })], providers: [CourseRepo], }).compile(); repo = module.get(CourseRepo); diff --git a/apps/server/src/shared/repo/coursegroup/coursegroup.repo.integration.spec.ts b/apps/server/src/shared/repo/coursegroup/coursegroup.repo.integration.spec.ts index 68f33db18c5..badad08671b 100644 --- a/apps/server/src/shared/repo/coursegroup/coursegroup.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/coursegroup/coursegroup.repo.integration.spec.ts @@ -20,7 +20,7 @@ describe('course group repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [Course, CourseGroup] })], providers: [CourseGroupRepo], }).compile(); repo = module.get(CourseGroupRepo); diff --git a/apps/server/src/shared/repo/dashboard/dashboard.model.mapper.spec.ts b/apps/server/src/shared/repo/dashboard/dashboard.model.mapper.spec.ts index 4b8bb7d732e..b54d7984f39 100644 --- a/apps/server/src/shared/repo/dashboard/dashboard.model.mapper.spec.ts +++ b/apps/server/src/shared/repo/dashboard/dashboard.model.mapper.spec.ts @@ -3,10 +3,12 @@ import { InternalServerErrorException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Course, + CourseGroup, DashboardEntity, DashboardGridElementModel, DashboardModelEntity, GridElement, + User, } from '@shared/domain/entity'; import { LearnroomMetadata, LearnroomTypes } from '@shared/domain/types'; import { MongoMemoryDatabaseModule } from '@testing/database'; @@ -21,7 +23,11 @@ describe('dashboard model mapper', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [DashboardModelEntity, DashboardGridElementModel, User, Course, CourseGroup], + }), + ], providers: [DashboardModelMapper], }).compile(); diff --git a/apps/server/src/shared/repo/dashboard/dashboard.repo.integration.spec.ts b/apps/server/src/shared/repo/dashboard/dashboard.repo.integration.spec.ts index b7030d9096d..f3983496d36 100644 --- a/apps/server/src/shared/repo/dashboard/dashboard.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/dashboard/dashboard.repo.integration.spec.ts @@ -1,6 +1,14 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { DashboardEntity, DashboardGridElementModel, GridElement } from '@shared/domain/entity'; +import { + Course, + CourseGroup, + DashboardEntity, + DashboardGridElementModel, + DashboardModelEntity, + GridElement, + User, +} from '@shared/domain/entity'; import { MongoMemoryDatabaseModule } from '@testing/database'; import { courseFactory } from '@testing/factory/course.factory'; import { userFactory } from '@testing/factory/user.factory'; @@ -14,7 +22,11 @@ describe('dashboard repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [DashboardEntity, DashboardModelEntity, DashboardGridElementModel, Course, User, CourseGroup], + }), + ], providers: [DashboardRepo, DashboardModelMapper], }).compile(); diff --git a/apps/server/src/shared/repo/dashboard/dashboardElement.repo.spec.ts b/apps/server/src/shared/repo/dashboard/dashboardElement.repo.spec.ts index badc6a5f299..06a593276e8 100644 --- a/apps/server/src/shared/repo/dashboard/dashboardElement.repo.spec.ts +++ b/apps/server/src/shared/repo/dashboard/dashboardElement.repo.spec.ts @@ -1,6 +1,6 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity'; +import { Course, CourseGroup, DashboardGridElementModel, DashboardModelEntity, User } from '@shared/domain/entity'; import { MongoMemoryDatabaseModule } from '@testing/database'; import { courseFactory } from '@testing/factory/course.factory'; import { userFactory } from '@testing/factory/user.factory'; @@ -13,7 +13,11 @@ describe(DashboardElementRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [DashboardGridElementModel, DashboardModelEntity, Course, User, CourseGroup], + }), + ], providers: [DashboardElementRepo], }).compile(); diff --git a/apps/server/src/shared/repo/externaltool/external-tool.repo.spec.ts b/apps/server/src/shared/repo/externaltool/external-tool.repo.spec.ts index dd114506c0d..1ac572beed8 100644 --- a/apps/server/src/shared/repo/externaltool/external-tool.repo.spec.ts +++ b/apps/server/src/shared/repo/externaltool/external-tool.repo.spec.ts @@ -28,7 +28,7 @@ describe(ExternalToolRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [ExternalToolEntity] })], providers: [ ExternalToolRepo, ExternalToolRepoMapper, diff --git a/apps/server/src/shared/repo/federalstate/federal-state.repo.spec.ts b/apps/server/src/shared/repo/federalstate/federal-state.repo.spec.ts index d6ed3455080..4dc358447a4 100644 --- a/apps/server/src/shared/repo/federalstate/federal-state.repo.spec.ts +++ b/apps/server/src/shared/repo/federalstate/federal-state.repo.spec.ts @@ -13,7 +13,7 @@ describe('FederalStateRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [FederalStateEntity] })], providers: [FederalStateRepo], }).compile(); repo = module.get(FederalStateRepo); diff --git a/apps/server/src/shared/repo/legacy-board/legacy-board.repo.spec.ts b/apps/server/src/shared/repo/legacy-board/legacy-board.repo.spec.ts index 93624071c28..8a7d158d891 100644 --- a/apps/server/src/shared/repo/legacy-board/legacy-board.repo.spec.ts +++ b/apps/server/src/shared/repo/legacy-board/legacy-board.repo.spec.ts @@ -1,6 +1,18 @@ import { EntityManager } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { LegacyBoard, LessonEntity, Task } from '@shared/domain/entity'; +import { + Course, + CourseGroup, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + LessonEntity, + Material, + SchoolEntity, + Submission, + Task, + TaskBoardElement, +} from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { boardFactory } from '@testing/factory/board.factory'; import { courseFactory } from '@testing/factory/course.factory'; @@ -18,7 +30,23 @@ describe('LegacyRoomBoardRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [ + LegacyBoard, + Course, + LegacyBoardElement, + CourseGroup, + SchoolEntity, + Submission, + Task, + LessonEntity, + Material, + TaskBoardElement, + LessonBoardElement, + ], + }), + ], providers: [LegacyBoardRepo], }).compile(); repo = module.get(LegacyBoardRepo); diff --git a/apps/server/src/shared/repo/ltitool/ltitool.repo.spec.ts b/apps/server/src/shared/repo/ltitool/ltitool.repo.spec.ts index 08ad29ec317..c134fee15d5 100644 --- a/apps/server/src/shared/repo/ltitool/ltitool.repo.spec.ts +++ b/apps/server/src/shared/repo/ltitool/ltitool.repo.spec.ts @@ -28,7 +28,7 @@ describe('LtiTool Repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [LtiTool] })], providers: [ LtiToolRepoSpec, { diff --git a/apps/server/src/shared/repo/materials/materials.repo.integration.spec.ts b/apps/server/src/shared/repo/materials/materials.repo.integration.spec.ts index 19f93ce3cf1..c9921130a42 100644 --- a/apps/server/src/shared/repo/materials/materials.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/materials/materials.repo.integration.spec.ts @@ -12,7 +12,7 @@ describe('MaterialsRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [Material] })], providers: [MaterialsRepo], }).compile(); diff --git a/apps/server/src/shared/repo/news/news.repo.integration.spec.ts b/apps/server/src/shared/repo/news/news.repo.integration.spec.ts index cefda99afe0..283e8916171 100644 --- a/apps/server/src/shared/repo/news/news.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/news/news.repo.integration.spec.ts @@ -1,7 +1,7 @@ import { NotFoundError } from '@mikro-orm/core'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { News } from '@shared/domain/entity'; +import { Course, CourseGroup, CourseNews, News, SchoolNews, TeamEntity, TeamNews } from '@shared/domain/entity'; import { SortOrder } from '@shared/domain/interface'; import { NewsTargetModel } from '@shared/domain/types'; import { cleanupCollections } from '@testing/cleanup-collections'; @@ -24,7 +24,11 @@ describe('NewsRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [News, TeamEntity, Course, CourseGroup, CourseNews, SchoolNews, TeamNews], + }), + ], providers: [NewsRepo], }).compile(); diff --git a/apps/server/src/shared/repo/role/role.repo.integration.spec.ts b/apps/server/src/shared/repo/role/role.repo.integration.spec.ts index 4e8bd793177..30a96f05271 100644 --- a/apps/server/src/shared/repo/role/role.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/role/role.repo.integration.spec.ts @@ -16,7 +16,7 @@ describe('role repo', () => { beforeAll(async () => { // em.clear do not clear the resultCache, it must be disabled for this test module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot({ resultCache: { adapter: NullCacheAdapter } })], + imports: [MongoMemoryDatabaseModule.forRoot({ resultCache: { adapter: NullCacheAdapter }, entities: [Role] })], providers: [RoleRepo], }).compile(); repo = module.get(RoleRepo); diff --git a/apps/server/src/shared/repo/school/legacy-school.repo.integration.spec.ts b/apps/server/src/shared/repo/school/legacy-school.repo.integration.spec.ts index edf06f9ee24..728c834ee85 100644 --- a/apps/server/src/shared/repo/school/legacy-school.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/school/legacy-school.repo.integration.spec.ts @@ -28,7 +28,11 @@ describe('LegacySchoolRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [SchoolEntity], + }), + ], providers: [ LegacySchoolRepo, { diff --git a/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.spec.ts b/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.spec.ts index afee39f9fc4..7bdf39bf0b3 100644 --- a/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.spec.ts +++ b/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.spec.ts @@ -9,7 +9,7 @@ import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/ent import { schoolExternalToolEntityFactory, schoolExternalToolFactory } from '@modules/tool/school-external-tool/testing'; import { SchoolExternalToolQuery } from '@modules/tool/school-external-tool/uc/dto/school-external-tool.types'; import { Test, TestingModule } from '@nestjs/testing'; -import { type SchoolEntity } from '@shared/domain/entity'; +import { SchoolEntity } from '@shared/domain/entity'; import { ExternalToolRepoMapper } from '@shared/repo/externaltool/external-tool.repo.mapper'; import { cleanupCollections } from '@testing/cleanup-collections'; import { MongoMemoryDatabaseModule } from '@testing/database'; @@ -23,7 +23,9 @@ describe(SchoolExternalToolRepo.name, () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ entities: [SchoolExternalToolEntity, ExternalToolEntity, SchoolEntity] }), + ], providers: [ SchoolExternalToolRepo, ExternalToolRepoMapper, @@ -48,7 +50,7 @@ describe(SchoolExternalToolRepo.name, () => { const createTools = () => { const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId(); - const school: SchoolEntity = schoolEntityFactory.buildWithId(); + const school = schoolEntityFactory.buildWithId(); const schoolExternalTool1: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ tool: externalToolEntity, school, diff --git a/apps/server/src/shared/repo/storageprovider/storageprovider.repo.spec.ts b/apps/server/src/shared/repo/storageprovider/storageprovider.repo.spec.ts index 1331330cd8c..4f8bb288541 100644 --- a/apps/server/src/shared/repo/storageprovider/storageprovider.repo.spec.ts +++ b/apps/server/src/shared/repo/storageprovider/storageprovider.repo.spec.ts @@ -13,7 +13,7 @@ describe('StorageProviderRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [StorageProviderEntity] })], providers: [StorageProviderRepo], }).compile(); diff --git a/apps/server/src/shared/repo/submission/submission.repo.integration.spec.ts b/apps/server/src/shared/repo/submission/submission.repo.integration.spec.ts index 5f4a08b4b07..93011269938 100644 --- a/apps/server/src/shared/repo/submission/submission.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/submission/submission.repo.integration.spec.ts @@ -1,6 +1,6 @@ import { EntityManager } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { Submission } from '@shared/domain/entity'; +import { Course, CourseGroup, LessonEntity, Material, Submission, Task } from '@shared/domain/entity'; import { cleanupCollections } from '@testing/cleanup-collections'; import { MongoMemoryDatabaseModule } from '@testing/database'; import { courseFactory } from '@testing/factory/course.factory'; @@ -18,7 +18,11 @@ describe('submission repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [Submission, Task, LessonEntity, Course, CourseGroup, Material], + }), + ], providers: [SubmissionRepo], }).compile(); repo = module.get(SubmissionRepo); diff --git a/apps/server/src/shared/repo/task/task.repo.integration.spec.ts b/apps/server/src/shared/repo/task/task.repo.integration.spec.ts index f4c5ba3e1ff..5afd9e56713 100644 --- a/apps/server/src/shared/repo/task/task.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/task/task.repo.integration.spec.ts @@ -1,6 +1,6 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { Task } from '@shared/domain/entity'; +import { Course, CourseGroup, LessonEntity, Material, Submission, Task } from '@shared/domain/entity'; import { SortOrder } from '@shared/domain/interface'; import { cleanupCollections } from '@testing/cleanup-collections'; import { MongoMemoryDatabaseModule } from '@testing/database'; @@ -22,7 +22,11 @@ describe('TaskRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [ + MongoMemoryDatabaseModule.forRoot({ + entities: [Task, Course, CourseGroup, LessonEntity, Submission, Material], + }), + ], providers: [TaskRepo], }).compile(); diff --git a/apps/server/src/shared/repo/teams/team.repo.integration.spec.ts b/apps/server/src/shared/repo/teams/team.repo.integration.spec.ts index 085453a6540..56622e41134 100644 --- a/apps/server/src/shared/repo/teams/team.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/teams/team.repo.integration.spec.ts @@ -17,7 +17,7 @@ describe('team repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [TeamEntity] })], providers: [TeamsRepo], }).compile(); repo = module.get(TeamsRepo); diff --git a/apps/server/src/shared/repo/user/user-do.repo.integration.spec.ts b/apps/server/src/shared/repo/user/user-do.repo.integration.spec.ts index 04f4ecbb3da..80bc86d3868 100644 --- a/apps/server/src/shared/repo/user/user-do.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/user/user-do.repo.integration.spec.ts @@ -29,7 +29,7 @@ describe('UserRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [User] })], providers: [ UserDORepo, { diff --git a/apps/server/src/shared/repo/user/user.repo.integration.spec.ts b/apps/server/src/shared/repo/user/user.repo.integration.spec.ts index 91a0ebc435f..ad5776952b7 100644 --- a/apps/server/src/shared/repo/user/user.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/user/user.repo.integration.spec.ts @@ -20,7 +20,7 @@ describe('user repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [User] })], providers: [UserRepo], }).compile(); repo = module.get(UserRepo); diff --git a/apps/server/src/shared/repo/userloginmigration/user-login-migration.repo.integration.spec.ts b/apps/server/src/shared/repo/userloginmigration/user-login-migration.repo.integration.spec.ts index 49c6c9a6f9e..d24934e14a1 100644 --- a/apps/server/src/shared/repo/userloginmigration/user-login-migration.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/userloginmigration/user-login-migration.repo.integration.spec.ts @@ -21,7 +21,7 @@ describe('UserLoginMigrationRepo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [UserLoginMigrationEntity] })], providers: [ UserLoginMigrationRepo, { diff --git a/apps/server/src/shared/repo/videoconference/video-conference.repo.spec.ts b/apps/server/src/shared/repo/videoconference/video-conference.repo.spec.ts index 636c66a6e3b..51c3aa7e4ff 100644 --- a/apps/server/src/shared/repo/videoconference/video-conference.repo.spec.ts +++ b/apps/server/src/shared/repo/videoconference/video-conference.repo.spec.ts @@ -28,7 +28,7 @@ describe('Video Conference Repo', () => { beforeAll(async () => { module = await Test.createTestingModule({ - imports: [MongoMemoryDatabaseModule.forRoot()], + imports: [MongoMemoryDatabaseModule.forRoot({ entities: [VideoConference] })], providers: [ VideoConferenceRepoSpec, { diff --git a/apps/server/src/testing/database/mongo-memory-database.module.ts b/apps/server/src/testing/database/mongo-memory-database.module.ts index cd18385473d..3d492a6abe6 100644 --- a/apps/server/src/testing/database/mongo-memory-database.module.ts +++ b/apps/server/src/testing/database/mongo-memory-database.module.ts @@ -1,8 +1,9 @@ import { MikroORM } from '@mikro-orm/core'; import { MikroOrmModule, MikroOrmModuleAsyncOptions } from '@mikro-orm/nestjs'; import { DynamicModule, Inject, Module, OnModuleDestroy } from '@nestjs/common'; -import { ALL_ENTITIES } from '@shared/domain/entity'; import _ from 'lodash'; + +import { defineConfig } from '@mikro-orm/mongodb'; import { MongoDatabaseModuleOptions } from './types'; const dbName = (): string => _.times(20, () => _.random(35).toString(36)).join(''); @@ -12,12 +13,12 @@ const createMikroOrmModule = (options: MikroOrmModuleAsyncOptions): DynamicModul useFactory: () => { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions, no-process-env const clientUrl = `${process.env.MONGO_TEST_URI}/${dbName()}`; - return { + return defineConfig({ allowGlobalContext: true, // can be overridden by options ...options, type: 'mongo', clientUrl, - }; + }); }, }); @@ -29,12 +30,9 @@ export class MongoMemoryDatabaseModule implements OnModuleDestroy { constructor(@Inject(MikroORM) private orm: MikroORM) {} public static forRoot(options?: MongoDatabaseModuleOptions): DynamicModule { - const defaultOptions = { - entities: ALL_ENTITIES, - }; return { module: MongoMemoryDatabaseModule, - imports: [createMikroOrmModule({ ...defaultOptions, ...options })], + imports: [createMikroOrmModule({ ...options })], exports: [MikroOrmModule], }; } diff --git a/apps/server/src/testing/factory/user-and-account.test.factory.ts b/apps/server/src/testing/factory/user-and-account.test.factory.ts index 76af4652dfa..43806fd8043 100644 --- a/apps/server/src/testing/factory/user-and-account.test.factory.ts +++ b/apps/server/src/testing/factory/user-and-account.test.factory.ts @@ -1,7 +1,8 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { accountFactory } from '@modules/account/testing/account.factory'; -import { SchoolEntity, User } from '@shared/domain/entity'; +import { SchoolEntity } from '@shared/domain/entity/school.entity'; +import { User } from '@shared/domain/entity/user.entity'; import { LanguageType, Permission } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; import _ from 'lodash'; @@ -31,7 +32,7 @@ export class UserAndAccountTestFactory { private static buildAccount(user: User, params: UserAndAccountParams = {}): AccountEntity { const accountParams = _.pick(params, 'username', 'systemId'); - const account = accountFactory.withUser(user).build(accountParams); + const account = accountFactory.withUser(user).buildWithId(accountParams); return account; } diff --git a/apps/server/src/testing/factory/user.factory.ts b/apps/server/src/testing/factory/user.factory.ts index 811f68ccc81..7e1bfcda83d 100644 --- a/apps/server/src/testing/factory/user.factory.ts +++ b/apps/server/src/testing/factory/user.factory.ts @@ -51,19 +51,19 @@ const consentFactory = BaseFactory.define(ConsentE }); class UserFactory extends BaseFactory { - withRoleByName(name: RoleName): this { + public withRoleByName(name: RoleName): this { const params: DeepPartial = { roles: [roleFactory.buildWithId({ name })] }; return this.params(params); } - withRole(role: Role): this { + public withRole(role: Role): this { const params: DeepPartial = { roles: [role] }; return this.params(params); } - asStudent(additionalPermissions: Permission[] = []): this { + public asStudent(additionalPermissions: Permission[] = []): this { const permissions = _.union(userPermissions, studentPermissions, additionalPermissions); const role = roleFactory.buildWithId({ permissions, name: RoleName.STUDENT }); @@ -72,7 +72,7 @@ class UserFactory extends BaseFactory { return this.params(params); } - asTeacher(additionalPermissions: Permission[] = []): this { + public asTeacher(additionalPermissions: Permission[] = []): this { const permissions = _.union(userPermissions, teacherPermissions, additionalPermissions); const role = roleFactory.buildWithId({ permissions, name: RoleName.TEACHER }); @@ -81,7 +81,7 @@ class UserFactory extends BaseFactory { return this.params(params); } - asAdmin(additionalPermissions: Permission[] = []): this { + public asAdmin(additionalPermissions: Permission[] = []): this { const permissions = _.union(userPermissions, adminPermissions, additionalPermissions); const role = roleFactory.buildWithId({ permissions, name: RoleName.ADMINISTRATOR }); @@ -90,7 +90,7 @@ class UserFactory extends BaseFactory { return this.params(params); } - asSuperhero(additionalPermissions: Permission[] = []): this { + public asSuperhero(additionalPermissions: Permission[] = []): this { const permissions = _.union(userPermissions, superheroPermissions, additionalPermissions); const role = roleFactory.buildWithId({ permissions, name: RoleName.SUPERHERO }); @@ -106,7 +106,7 @@ export const userFactory = UserFactory.define(User, ({ sequence }) => { lastName: `Doe ${sequence}`, email: `user-${sequence}@example.com`, roles: [], - school: schoolEntityFactory.build(), + school: schoolEntityFactory.buildWithId(), consent: consentFactory.build(), secondarySchools: [], }; diff --git a/apps/server/src/testing/setup-entities.ts b/apps/server/src/testing/setup-entities.ts index 552b66257e0..a20491462ef 100644 --- a/apps/server/src/testing/setup-entities.ts +++ b/apps/server/src/testing/setup-entities.ts @@ -1,11 +1,123 @@ +/* eslint-disable @typescript-eslint/no-restricted-imports */ +// Remove the eslint-disable line after fixing the import issue in BC-8880 import { AnyEntity, EntityClass, MikroORM } from '@mikro-orm/core'; -import { ALL_ENTITIES } from '@shared/domain/entity'; +import { AccountEntity } from '@modules/account/domain/entity/account.entity'; +import { BoardNodeEntity } from '@modules/board/repo/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { DeletionLogEntity } from '@modules/deletion/repo/entity/deletion-log.entity'; +import { DeletionRequestEntity } from '@modules/deletion/repo/entity/deletion-request.entity'; +import { GroupEntity } from '@modules/group/entity'; +import { InstanceEntity } from '@modules/instance'; +import { SchoolSystemOptionsEntity } from '@modules/legacy-school/entity'; +import { MediaSourceEntity } from '@modules/media-source/entity'; +import { OauthSessionTokenEntity } from '@modules/oauth/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { RegistrationPinEntity } from '@modules/registration-pin/entity'; +import { RocketChatUserEntity } from '@modules/rocketchat-user/entity'; +import { RoomMembershipEntity } from '@modules/room-membership/repo/entity/room-membership.entity'; +import { RoomEntity } from '@modules/room/repo/entity'; +import { MediaSchoolLicenseEntity, SchoolLicenseEntity } from '@modules/school-license/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { SystemEntity } from '@modules/system/entity/system.entity'; +import { ContextExternalToolEntity, LtiDeepLinkTokenEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { ImportUser } from '@modules/user-import/entity'; +import { MediaUserLicenseEntity, UserLicenseEntity } from '@modules/user-license/entity'; +import { ColumnBoardNode } from '@shared/domain/entity/column-board-node.entity'; +import { Course } from '@shared/domain/entity/course.entity'; +import { CourseGroup } from '@shared/domain/entity/coursegroup.entity'; +import { DashboardGridElementModel, DashboardModelEntity } from '@shared/domain/entity/dashboard.model.entity'; +import { CountyEmbeddable, FederalStateEntity } from '@shared/domain/entity/federal-state.entity'; +import { + ColumnboardBoardElement, + LegacyBoard, + LegacyBoardElement, + LessonBoardElement, + TaskBoardElement, +} from '@shared/domain/entity/legacy-board'; +import { LessonEntity } from '@shared/domain/entity/lesson.entity'; +import { LtiTool } from '@shared/domain/entity/ltitool.entity'; +import { Material } from '@shared/domain/entity/materials.entity'; +import { CourseNews, News, SchoolNews, TeamNews } from '@shared/domain/entity/news.entity'; +import { Role } from '@shared/domain/entity/role.entity'; +import { SchoolEntity, SchoolRolePermission, SchoolRoles } from '@shared/domain/entity/school.entity'; +import { SchoolYearEntity } from '@shared/domain/entity/schoolyear.entity'; +import { StorageProviderEntity } from '@shared/domain/entity/storageprovider.entity'; +import { Submission } from '@shared/domain/entity/submission.entity'; +import { Task } from '@shared/domain/entity/task.entity'; +import { TeamEntity, TeamUserEntity } from '@shared/domain/entity/team.entity'; +import { UserLoginMigrationEntity } from '@shared/domain/entity/user-login-migration.entity'; +import { User } from '@shared/domain/entity/user.entity'; +import { VideoConference } from '@shared/domain/entity/video-conference.entity'; + +export const ENTITIES = [ + AccountEntity, + LegacyBoard, + LegacyBoardElement, + BoardNodeEntity, + ColumnboardBoardElement, + ColumnBoardNode, + ClassEntity, + DeletionRequestEntity, + DeletionLogEntity, + ContextExternalToolEntity, + CountyEmbeddable, + Course, + CourseGroup, + CourseNews, + DashboardGridElementModel, + DashboardModelEntity, + ExternalToolEntity, + FederalStateEntity, + ImportUser, + LessonEntity, + LessonBoardElement, + LtiTool, + Material, + News, + PseudonymEntity, + ExternalToolPseudonymEntity, + RocketChatUserEntity, + Role, + RoomEntity, + RoomMembershipEntity, + SchoolEntity, + SchoolExternalToolEntity, + SchoolNews, + SchoolRolePermission, + SchoolRoles, + SchoolSystemOptionsEntity, + SchoolYearEntity, + ShareToken, + StorageProviderEntity, + Submission, + SystemEntity, + Task, + TaskBoardElement, + TeamEntity, + TeamNews, + TeamUserEntity, + User, + UserLoginMigrationEntity, + VideoConference, + GroupEntity, + RegistrationPinEntity, + UserLicenseEntity, + MediaUserLicenseEntity, + InstanceEntity, + MediaSourceEntity, + SchoolLicenseEntity, + MediaSchoolLicenseEntity, + OauthSessionTokenEntity, + LtiDeepLinkTokenEntity, +]; /** * Test-Setup to make all entities available without a database connection. * @returns */ -export const setupEntities = async (entities: EntityClass[] = ALL_ENTITIES): Promise => { +export const setupEntities = async (entities: EntityClass[] = ENTITIES): Promise => { const orm = await MikroORM.init({ type: 'mongo', dbName: 'dummy', diff --git a/apps/server/src/testing/test-api-client.ts b/apps/server/src/testing/test-api-client.ts index 61c12308412..5587c2c401b 100644 --- a/apps/server/src/testing/test-api-client.ts +++ b/apps/server/src/testing/test-api-client.ts @@ -1,8 +1,10 @@ import type { AccountEntity } from '@modules/account/domain/entity/account.entity'; import { defaultTestPassword } from '@modules/account/testing/account.factory'; import { INestApplication } from '@nestjs/common'; +import { User } from '@shared/domain/entity'; import type { Server } from 'node:net'; import supertest, { Response } from 'supertest'; +import { JwtAuthenticationFactory } from './factory/jwt-authentication.factory'; interface AuthenticationResponse { accessToken: string; @@ -126,6 +128,27 @@ export class TestApiClient { ); } + public loginByUser(account: AccountEntity, user: User): this { + const jwt = JwtAuthenticationFactory.createJwt({ + accountId: account.id, + userId: user.id, + schoolId: user.school.id, + roles: [user.roles[0].id], + support: false, + isExternalUser: false, + }); + + return new (this.constructor as new (app: INestApplication, baseRoute: string, authValue: string) => this)( + this.app, + this.baseRoute, + jwt + ); + } + + public getAuthHeader(): string { + return this.authHeader; + } + private isSlash(inputPath: string, pos: number): boolean { const isSlash = inputPath.charAt(pos) === '/'; diff --git a/package.json b/package.json index d584b24d092..7b3879ac47f 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,8 @@ "mikro-orm": { "useTsNode": true, "configPaths": [ - "./apps/server/src/config/mikro-orm-cli.config.ts", - "./dist/server/config/mikro-orm-cli.config.js" + "./apps/server/src/modules/management/mikro-orm-cli.config.ts", + "./dist/server/modules/management/mikro-orm-cli.config.js" ] }, "scripts": { @@ -344,4 +344,4 @@ "tsconfig-paths": "^4.2.0", "typescript": "^5.7.3" } -} +} \ No newline at end of file diff --git a/sonar-project.properties b/sonar-project.properties index 9523dcb7394..b3379028c56 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -5,6 +5,6 @@ sonar.tests=. sonar.test.inclusions=**/*.spec.ts sonar.exclusions=**/*.js,jest.config.ts,globalSetup.ts,globalTeardown.ts,**/*.app.ts,**/seed-data/**/*.ts,**/migrations/mikro-orm/*.ts,**/etherpad-api-client/**/*.ts,**/authorization-api-client/**/*.ts,**/board-api-client/**/*.ts,**/generated/**/*.ts,**/room-api-client/**/*.ts,**/cards-api-client/**/*.ts,**/lessons-api-client/**/*.ts sonar.coverage.exclusions=**/board-management.uc.ts,**/*.module.ts,**/*.factory.ts,**/testing/**/*.ts,**/migrations/mikro-orm/*.ts,**/globalSetup.ts,**/globalTeardown.ts,**/etherpad-api-client/**/*.ts,**/authorization-api-client/**/*.ts,**/board-api-client/**/*.ts,**/generated/**/*.ts,**/room-api-client/**/*.ts,apps/server/src/console/console.ts -sonar.cpd.exclusions=**/controller/dto/**/*.ts,**/api/dto/**/*.ts,**/testing/factory/*.factory.ts,**/modules/common-cartridge/**/*.ts +sonar.cpd.exclusions=**/controller/dto/**/*.ts,**/api/dto/**/*.ts,**/testing/factory/*.factory.ts,**/modules/common-cartridge/**/*.ts,**/testing/setup-entities.ts sonar.javascript.lcov.reportPaths=merged-lcov.info sonar.typescript.tsconfigPaths=tsconfig.json,src/apps/server/tsconfig.app.json diff --git a/test/utils/setup.nest.services.js b/test/utils/setup.nest.services.js index f19cb62ecc5..77ed748414a 100644 --- a/test/utils/setup.nest.services.js +++ b/test/utils/setup.nest.services.js @@ -5,17 +5,18 @@ const { ConfigModule } = require('@nestjs/config'); // run 'npm run nest:build' for the following imports to work, // this is a workaround to make TypeScript modules available in JavaScript +const { defineConfig } = require('@mikro-orm/mongodb'); const { AccountApiModule } = require('../../dist/apps/server/modules/account/account-api.module'); const { AccountUc } = require('../../dist/apps/server/modules/account/api/account.uc'); const { AccountService } = require('../../dist/apps/server/modules/account/domain/services/account.service'); const { DB_PASSWORD, DB_URL, DB_USERNAME } = require('../../dist/apps/server/imports-from-feathers'); -const { ALL_ENTITIES } = require('../../dist/apps/server/shared/domain/entity/all-entities'); const { TeamService } = require('../../dist/apps/server/modules/teams/service/team.service'); const { TeamsApiModule } = require('../../dist/apps/server/modules/teams/teams-api.module'); const { AuthorizationModule } = require('../../dist/apps/server/modules/authorization'); const { SystemRule, AuthorizationRulesModule } = require('../../dist/apps/server/modules/authorization-rules'); const { createConfigModuleOptions } = require('../../dist/apps/server/shared/common/config-module-options'); const { serverConfig } = require('../../dist/apps/server/modules/server/server.config'); +const { TEST_ENTITIES } = require('../../dist/apps/server/modules/server/server.entity.imports'); const { RosterModule } = require('../../dist/apps/server/modules/roster/roster.module'); const { FeathersRosterService } = require('../../dist/apps/server/modules/roster/service/feathers-roster.service'); const { RabbitMQWrapperTestModule } = require('../../dist/apps/server/infra/rabbitmq/rabbitmq.module'); @@ -24,15 +25,17 @@ const setupNestServices = async (app) => { const module = await Test.createTestingModule({ imports: [ RabbitMQWrapperTestModule, - MikroOrmModule.forRoot({ - type: 'mongo', - clientUrl: DB_URL, - password: DB_PASSWORD, - user: DB_USERNAME, - entities: ALL_ENTITIES, - allowGlobalContext: true, - // debug: true, // use it for locally debugging of querys - }), + MikroOrmModule.forRoot( + defineConfig({ + type: 'mongo', + clientUrl: DB_URL, + password: DB_PASSWORD, + user: DB_USERNAME, + entities: TEST_ENTITIES, + allowGlobalContext: true, + // debug: true, // use it for locally debugging of querys + }) + ), ConfigModule.forRoot(createConfigModuleOptions(serverConfig)), AccountApiModule, TeamsApiModule,