-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SPSH-657: Schulen und Lehrer per Event ins LDAP kopieren (#510)
* start persisting schools on creation in LDAP via EventBus * implement event for CreatePersonenKontext (Lehrer on school) * create LDAP-TestModule * test for ldap-event-handler * add missing tests, remove obsolete ones * fix lint * adjust test cases for LDAP Client Svc * adjust docu about test with LDAP * fix lint * adjust coverageProvider to babel, adjust tests * add test for ProviderController (allServiceProviders) * add new events for DeleteSchule and DeletePersonenkontext * fix lint in ldap-failure-test * remove dbiam-personenuebersicht-scope * adjust ldap tests * Update tests for person-scope, oidc-strategy and authentication-controller * exclude redisClient in server.module from test-coverage * fix lint * adjust tests for ldap person methods * fix lint * adjust ldpa person methods test again * tests for ldap-client-svc * renamed events, use aggregates instead of Dtos * adjust tests for ldpa methods when using aggregates * adjust tests for ldap again * add test stuff for ldap * adjust tests for LdapClientSvc to use mocks * mock client in LdapClient and bind method * use person referrer as uid for LDAP * Wrapped a mutex around the LDAP-Calls since openldap doesn't seem to like parallel requests from the same client * add SchuleKennungEindeutig specification * renamed events classes --------- Co-authored-by: Marvin Rode <[email protected]> Co-authored-by: Kristoff Kiefer <[email protected]> Co-authored-by: Kristoff Kiefer <[email protected]>
- Loading branch information
1 parent
9b01c04
commit f0cf28c
Showing
40 changed files
with
1,512 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Test with LDAP / LDAP Test Module | ||
|
||
## Order of tests / dependencies | ||
|
||
Because some entries in LDAP dependent on others, testing requires a little bit preparation. | ||
Sometimes it makes sense to define a `beforeAll` for individual tests (describe or it). | ||
|
||
Currently, for each method of `LdapClientService` for example a separate test is done in one separate file | ||
and each single test case (describe or it) should be tested separately whether it returns the expected result. | ||
|
||
## Waiting / timing issues | ||
|
||
The class `LdapClient` is used as wrapper, mainly to be able to mock the injection of this class-type and mock every | ||
call that would result in an *ADD* or *DEL* operation, because regardless of execution order and implicit waiting, sometimes | ||
an organisation simply does not exist (yet), when e.g. a teacher should be inserted in/under the corresponding OU. | ||
|
||
Currently, the tests which use `LdapTestModule` also import `DatabaseTestModule` and set up the database to ensure proper | ||
wait for the LDAP test-container to start. In the future this should be replaced by a proper wait-strategy. Unfortunately at the moment | ||
waiting for a simple health-check is not that easy. |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,314 @@ | ||
import { EntityManager, MikroORM } from '@mikro-orm/core'; | ||
import { INestApplication } from '@nestjs/common'; | ||
import { APP_PIPE } from '@nestjs/core'; | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { | ||
ConfigTestModule, | ||
DatabaseTestModule, | ||
DEFAULT_TIMEOUT_FOR_TESTCONTAINERS, | ||
LdapTestModule, | ||
MapperTestModule, | ||
} from '../../../../test/utils/index.js'; | ||
import { GlobalValidationPipe } from '../../../shared/validation/global-validation.pipe.js'; | ||
import { LdapConfigModule } from '../ldap-config.module.js'; | ||
import { LdapModule } from '../ldap.module.js'; | ||
import { faker } from '@faker-js/faker'; | ||
import { OrganisationsTyp } from '../../../modules/organisation/domain/organisation.enums.js'; | ||
import { LdapClientService } from './ldap-client.service.js'; | ||
import { Organisation } from '../../../modules/organisation/domain/organisation.js'; | ||
import { Person } from '../../../modules/person/domain/person.js'; | ||
import { createMock, DeepMocked } from '@golevelup/ts-jest'; | ||
import { LdapClient } from './ldap-client.js'; | ||
import { Client } from 'ldapts'; | ||
|
||
describe('LDAP Client Service', () => { | ||
let app: INestApplication; | ||
let module: TestingModule; | ||
let orm: MikroORM; | ||
let em: EntityManager; | ||
let ldapClientService: LdapClientService; | ||
let ldapClientMock: DeepMocked<LdapClient>; | ||
let clientMock: DeepMocked<Client>; | ||
|
||
let organisation: Organisation<true>; | ||
let invalidOrganisation: Organisation<true>; | ||
let person: Person<true>; | ||
let personWithoutReferrer: Person<true>; | ||
let ouKennung: string; | ||
|
||
beforeAll(async () => { | ||
module = await Test.createTestingModule({ | ||
imports: [ | ||
ConfigTestModule, | ||
DatabaseTestModule.forRoot({ isDatabaseRequired: true }), | ||
LdapModule, | ||
MapperTestModule, | ||
], | ||
providers: [ | ||
{ | ||
provide: APP_PIPE, | ||
useClass: GlobalValidationPipe, | ||
}, | ||
], | ||
}) | ||
.overrideModule(LdapConfigModule) | ||
.useModule(LdapTestModule.forRoot({ isLdapRequired: true })) | ||
.overrideProvider(LdapClient) | ||
.useValue(createMock<LdapClient>()) | ||
.compile(); | ||
|
||
orm = module.get(MikroORM); | ||
em = module.get(EntityManager); | ||
ldapClientService = module.get(LdapClientService); | ||
ldapClientMock = module.get(LdapClient); | ||
clientMock = createMock<Client>(); | ||
|
||
ouKennung = faker.string.numeric({ length: 7 }); | ||
organisation = Organisation.construct( | ||
faker.string.uuid(), | ||
faker.date.past(), | ||
faker.date.recent(), | ||
undefined, | ||
undefined, | ||
ouKennung, | ||
faker.company.name(), | ||
undefined, | ||
undefined, | ||
OrganisationsTyp.SCHULE, | ||
undefined, | ||
); | ||
invalidOrganisation = { | ||
id: faker.string.uuid(), | ||
name: faker.company.name(), | ||
kennung: undefined, | ||
typ: OrganisationsTyp.SCHULE, | ||
createdAt: faker.date.past(), | ||
updatedAt: faker.date.recent(), | ||
}; | ||
person = Person.construct( | ||
faker.string.uuid(), | ||
faker.date.past(), | ||
faker.date.recent(), | ||
faker.person.lastName(), | ||
faker.person.firstName(), | ||
'1', | ||
faker.lorem.word(), | ||
undefined, | ||
faker.string.uuid(), | ||
); | ||
personWithoutReferrer = Person.construct( | ||
faker.string.uuid(), | ||
faker.date.past(), | ||
faker.date.recent(), | ||
faker.person.lastName(), | ||
faker.person.firstName(), | ||
'1', | ||
faker.lorem.word(), | ||
undefined, | ||
undefined, | ||
); | ||
|
||
//currently only used to wait for the LDAP container, because setupDatabase() is blocking | ||
await DatabaseTestModule.setupDatabase(module.get(MikroORM)); | ||
app = module.createNestApplication(); | ||
await app.init(); | ||
}, DEFAULT_TIMEOUT_FOR_TESTCONTAINERS); | ||
|
||
afterAll(async () => { | ||
await DatabaseTestModule.clearDatabase(orm); | ||
await orm.close(); | ||
await app.close(); | ||
}); | ||
|
||
beforeEach(async () => { | ||
jest.resetAllMocks(); | ||
await DatabaseTestModule.clearDatabase(orm); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(em).toBeDefined(); | ||
}); | ||
|
||
describe('bind', () => { | ||
describe('when error is thrown inside', () => { | ||
it('should return error result', async () => { | ||
ldapClientMock.getClient.mockImplementation(() => { | ||
clientMock.bind.mockRejectedValueOnce(new Error()); | ||
clientMock.add.mockResolvedValueOnce(); | ||
return clientMock; | ||
}); | ||
|
||
const result: Result<Organisation<true>> = await ldapClientService.createOrganisation(organisation); | ||
|
||
expect(result.ok).toBeFalsy(); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('creation', () => { | ||
describe('organisation', () => { | ||
it('when called with valid organisation should return truthy result', async () => { | ||
ldapClientMock.getClient.mockImplementation(() => { | ||
clientMock.bind.mockResolvedValueOnce(); | ||
clientMock.add.mockResolvedValueOnce(); | ||
return clientMock; | ||
}); | ||
|
||
const result: Result<Organisation<true>> = await ldapClientService.createOrganisation(organisation); | ||
|
||
expect(result.ok).toBeTruthy(); | ||
}); | ||
|
||
it('when called with organisation without kennung should return error result', async () => { | ||
ldapClientMock.getClient.mockImplementation(() => { | ||
clientMock.bind.mockResolvedValueOnce(); | ||
clientMock.add.mockResolvedValueOnce(); | ||
return clientMock; | ||
}); | ||
const result: Result<Organisation<true>> = | ||
await ldapClientService.createOrganisation(invalidOrganisation); | ||
|
||
expect(result.ok).toBeFalsy(); | ||
}); | ||
}); | ||
|
||
describe('lehrer', () => { | ||
it('when called with valid person and organisation should return truthy result', async () => { | ||
ldapClientMock.getClient.mockImplementation(() => { | ||
clientMock.bind.mockResolvedValueOnce(); | ||
clientMock.add.mockResolvedValueOnce(); | ||
return clientMock; | ||
}); | ||
const result: Result<Person<true>> = await ldapClientService.createLehrer(person, organisation); | ||
|
||
expect(result.ok).toBeTruthy(); | ||
}); | ||
|
||
it('when called with valid person and an organisation without kennung should return error result', async () => { | ||
ldapClientMock.getClient.mockImplementation(() => { | ||
clientMock.bind.mockResolvedValueOnce(); | ||
clientMock.add.mockResolvedValueOnce(); | ||
return clientMock; | ||
}); | ||
const result: Result<Person<true>> = await ldapClientService.createLehrer(person, invalidOrganisation); | ||
|
||
expect(result.ok).toBeFalsy(); | ||
}); | ||
|
||
it('when called with person without referrer should return error result', async () => { | ||
ldapClientMock.getClient.mockImplementation(() => { | ||
clientMock.bind.mockResolvedValueOnce(); | ||
clientMock.add.mockResolvedValueOnce(); | ||
return clientMock; | ||
}); | ||
const result: Result<Person<true>> = await ldapClientService.createLehrer( | ||
personWithoutReferrer, | ||
organisation, | ||
); | ||
|
||
expect(result.ok).toBeFalsy(); | ||
}); | ||
|
||
it('when bind returns error', async () => { | ||
ldapClientMock.getClient.mockImplementation(() => { | ||
clientMock.bind.mockRejectedValueOnce(new Error()); | ||
clientMock.add.mockResolvedValueOnce(); | ||
return clientMock; | ||
}); | ||
const result: Result<Person<true>> = await ldapClientService.createLehrer(person, organisation); | ||
|
||
expect(result.ok).toBeFalsy(); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('deletion', () => { | ||
describe('delete lehrer', () => { | ||
it('should return truthy result', async () => { | ||
ldapClientMock.getClient.mockImplementation(() => { | ||
clientMock.bind.mockResolvedValueOnce(); | ||
clientMock.del.mockResolvedValueOnce(); | ||
return clientMock; | ||
}); | ||
|
||
const result: Result<Person<true>> = await ldapClientService.deleteLehrer(person, organisation); | ||
|
||
expect(result.ok).toBeTruthy(); | ||
}); | ||
|
||
it('when called with valid person and an organisation without kennung should return error result', async () => { | ||
ldapClientMock.getClient.mockImplementation(() => { | ||
clientMock.bind.mockResolvedValueOnce(); | ||
clientMock.add.mockResolvedValueOnce(); | ||
return clientMock; | ||
}); | ||
const result: Result<Person<true>> = await ldapClientService.deleteLehrer(person, invalidOrganisation); | ||
|
||
expect(result.ok).toBeFalsy(); | ||
}); | ||
|
||
it('when called with person without referrer should return error result', async () => { | ||
ldapClientMock.getClient.mockImplementation(() => { | ||
clientMock.bind.mockResolvedValueOnce(); | ||
clientMock.add.mockResolvedValueOnce(); | ||
return clientMock; | ||
}); | ||
const result: Result<Person<true>> = await ldapClientService.deleteLehrer( | ||
personWithoutReferrer, | ||
organisation, | ||
); | ||
|
||
expect(result.ok).toBeFalsy(); | ||
}); | ||
|
||
it('when bind returns error', async () => { | ||
ldapClientMock.getClient.mockImplementation(() => { | ||
clientMock.bind.mockRejectedValueOnce(new Error()); | ||
clientMock.add.mockResolvedValueOnce(); | ||
return clientMock; | ||
}); | ||
const result: Result<Person<true>> = await ldapClientService.deleteLehrer(person, organisation); | ||
|
||
expect(result.ok).toBeFalsy(); | ||
}); | ||
}); | ||
|
||
describe('delete organisation', () => { | ||
it('when called with valid organisation should return truthy result', async () => { | ||
ldapClientMock.getClient.mockImplementation(() => { | ||
clientMock.bind.mockResolvedValueOnce(); | ||
clientMock.del.mockResolvedValueOnce(); | ||
return clientMock; | ||
}); | ||
|
||
const result: Result<Organisation<true>> = await ldapClientService.deleteOrganisation(organisation); | ||
|
||
expect(result.ok).toBeTruthy(); | ||
}); | ||
|
||
it('when called with organisation without kennung should return error result', async () => { | ||
ldapClientMock.getClient.mockImplementation(() => { | ||
clientMock.bind.mockResolvedValueOnce(); | ||
clientMock.add.mockResolvedValueOnce(); | ||
return clientMock; | ||
}); | ||
const result: Result<Organisation<true>> = | ||
await ldapClientService.deleteOrganisation(invalidOrganisation); | ||
|
||
expect(result.ok).toBeFalsy(); | ||
}); | ||
|
||
it('when bind returns error', async () => { | ||
ldapClientMock.getClient.mockImplementation(() => { | ||
clientMock.bind.mockRejectedValueOnce(new Error()); | ||
clientMock.add.mockResolvedValueOnce(); | ||
return clientMock; | ||
}); | ||
const result: Result<Organisation<true>> = | ||
await ldapClientService.deleteOrganisation(invalidOrganisation); | ||
|
||
expect(result.ok).toBeFalsy(); | ||
}); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.