anonymous-student
Version:
Anonymous student is used to retrieve and save information from our website users.
335 lines (264 loc) • 10.5 kB
text/typescript
import { IEventAggregationService } from '@studyportals/event-aggregation-service-interface';
import { StudentField } from '@studyportals/studentdomain';
import { suite, test } from '@testdeck/mocha';
import { assert } from 'chai';
import * as Moq from 'typemoq';
import { IMock } from 'typemoq/Api/IMock';
import { StudentRepositoryStateType } from '../../interfaces/enumerations';
import { AnonymousStudentEventBroadcaster } from '../../src/infrastructure/anonymous-student-event-broadcaster';
import { CachedStudentClient } from '../../src/infrastructure/clients/cached-student-client';
import { LocalStudentClient } from '../../src/infrastructure/clients/local-student-client';
import { StudentAPIClient } from '../../src/infrastructure/clients/student-api-client';
import { StudentClient } from '../../src/infrastructure/interfaces/student-client';
class CachedStudentClientTest {
protected testInstanceMock: IMock<CachedStudentClient>;
protected get testInstance(): CachedStudentClient {
return this.testInstanceMock.object;
}
protected studentAPIClientMock: IMock<StudentClient>;
protected get studentAPIClient(): StudentClient {
return this.studentAPIClientMock.object;
}
protected localStudentClientMock: IMock<StudentClient>;
protected get localStudentClient(): StudentClient {
return this.localStudentClientMock.object;
}
protected eventAggregationServiceMock: IMock<IEventAggregationService>;
protected get eventAggregationService(): IEventAggregationService {
return this.eventAggregationServiceMock.object;
}
protected anonymousStudentEventBroadcasterMock: IMock<AnonymousStudentEventBroadcaster>;
protected get anonymousStudentEventBroadcaster(): AnonymousStudentEventBroadcaster {
return this.anonymousStudentEventBroadcasterMock.object;
}
public before(): void {
this.studentAPIClientMock = Moq.Mock.ofType(StudentAPIClient);
this.localStudentClientMock = Moq.Mock.ofType(LocalStudentClient);
this.eventAggregationServiceMock = Moq.Mock.ofType<IEventAggregationService>();
this.anonymousStudentEventBroadcasterMock = Moq.Mock.ofType(AnonymousStudentEventBroadcaster);
const instance = new CachedStudentClient(
this.studentAPIClient as StudentAPIClient,
this.localStudentClient as LocalStudentClient,
this.eventAggregationService,
this.anonymousStudentEventBroadcaster,
);
this.testInstanceMock = Moq.Mock.ofInstance(instance);
this.testInstanceMock.callBase = true;
}
public async notify_Should_Not_Broadcast_When_Event_Is_Not_StudentProfileChanged(): Promise<void> {
// given
const providedEventType = 'SomeRandomEvent';
// when
await this.testInstance.notify({eventType: providedEventType} as any);
// then
this.anonymousStudentEventBroadcasterMock.verify((x) => x.broadcastProfileUpdatedEvent(Moq.It.isAny()), Moq.Times.never());
}
public async notify_Should_Broadcast_Correct_Changes_When_Called_With_Updates(): Promise<void> {
// given
const providedEvent = {
eventType: 'StudentProfileChanged',
updated: {
first_name: 'ABC',
last_name: 'XYZ',
},
};
const expectedEvent = {
state: StudentRepositoryStateType.ONLINE,
changes: providedEvent.updated,
} as any;
// when
await this.testInstance.notify(providedEvent as any);
// then
this.anonymousStudentEventBroadcasterMock.verify((x) => x.broadcastProfileUpdatedEvent(Moq.It.isObjectWith(expectedEvent)), Moq.Times.once());
}
public async notify_Should_Broadcast_Correct_Deletion_When_Called_With_Deletes(): Promise<void> {
// given
const providedEvent = {
eventType: 'StudentProfileChanged',
deleted: {
first_name: null,
last_name: null,
},
};
const expectedEvent = {
state: StudentRepositoryStateType.ONLINE,
isLocal: false,
changes: {
first_name: undefined,
last_name: undefined,
},
} as any;
// when
await this.testInstance.notify(providedEvent as any);
// then
this.anonymousStudentEventBroadcasterMock.verify((x) => x.broadcastProfileUpdatedEvent(Moq.It.isObjectWith(expectedEvent)), Moq.Times.once());
}
public async notify_Should_BroadcastIsLocalTrue_When_EventHashIsLocalHash(): Promise<void> {
// given
const providedEvent = {
eventType: 'StudentProfileChanged',
updated: {
[StudentField.FIRST_NAME]: 'ABC',
[StudentField.LAST_STATE_CHANGE_HASH]: 'someHash',
},
};
const expectedEvent = {
state: StudentRepositoryStateType.ONLINE,
isLocal: true,
changes: {
[StudentField.FIRST_NAME]: 'ABC',
[StudentField.LAST_STATE_CHANGE_HASH]: 'someHash',
},
} as any;
this.testInstance['localStateHashes'] = ['someHash'];
// when
await this.testInstance.notify(providedEvent as any);
// then
this.anonymousStudentEventBroadcasterMock.verify((x) => x.broadcastProfileUpdatedEvent(Moq.It.isObjectWith(expectedEvent)), Moq.Times.once());
}
public async notify_Should_Save_Changes_In_LocalStorage_When_Correct_Event(): Promise<void> {
// given
const providedEvent = {
eventType: 'StudentProfileChanged',
updated: {
last_login: 434,
},
deleted: {
first_name: null,
last_name: null,
},
};
const expectedData = {
last_login: 434,
first_name: undefined,
last_name: undefined,
};
// when
await this.testInstance.notify(providedEvent as any);
// then
this.localStudentClientMock.verify((x) => x.setData(Moq.It.isObjectWith(expectedData)), Moq.Times.once());
}
public async addToCollection_Should_AddToCollection_Remote_AND_Local_When_Called_With_Disciplines(): Promise<void> {
// given
const expectedIds = [1, 2, 3];
// when
await this.testInstance.addToCollection(StudentField.DISCIPLINES, expectedIds);
// then
this.studentAPIClientMock.verify((x) => x.addToCollection(StudentField.DISCIPLINES, expectedIds), Moq.Times.once());
this.localStudentClientMock.verify((x) => x.addToCollection(StudentField.DISCIPLINES, expectedIds), Moq.Times.once());
}
public async addToCollection_Should_AddLocalHash_When_Called_With_Disciplines(): Promise<void> {
// given
const expectedLastStateChangeHash = 'someSecretHash';
const expectedIds = [1, 2, 3];
this.studentAPIClientMock
.setup((x) => x.addToCollection(StudentField.DISCIPLINES, expectedIds))
.returns(async () => expectedLastStateChangeHash);
// when
await this.testInstance.addToCollection(StudentField.DISCIPLINES, expectedIds);
// then
assert.equal(this.testInstance['localStateHashes'][0], expectedLastStateChangeHash);
}
public async removeFromCollection_Should_RemoveFromCollection_Remote_AND_Local_When_Called_With_Disciplines(): Promise<void> {
// given
const expectedIds = [1, 2, 3];
// when
await this.testInstance.removeFromCollection(StudentField.DISCIPLINES, expectedIds);
// then
this.studentAPIClientMock.verify((x) => x.removeFromCollection(StudentField.DISCIPLINES, expectedIds), Moq.Times.once());
this.localStudentClientMock.verify((x) => x.removeFromCollection(StudentField.DISCIPLINES, expectedIds), Moq.Times.once());
}
public async removeFromCollection_Should_AddLocalHash_When_Called_With_Disciplines(): Promise<void> {
// given
const expectedLastStateChangeHash = 'someSecretHash';
const expectedIds = [1, 2, 3];
this.studentAPIClientMock
.setup((x) => x.removeFromCollection(StudentField.DISCIPLINES, expectedIds))
.returns(async () => expectedLastStateChangeHash);
// when
await this.testInstance.removeFromCollection(StudentField.DISCIPLINES, expectedIds);
// then
assert.equal(this.testInstance['localStateHashes'][0], expectedLastStateChangeHash);
}
public async setData_Should_SetData_Remote_AND_Local_WHen_Called_With_Data(): Promise<void> {
// given
const expectedData = {
first_name: 'SomeName',
};
// when
await this.testInstance.setData(expectedData);
// then
this.studentAPIClientMock.verify((x) => x.setData(expectedData), Moq.Times.once());
this.localStudentClientMock.verify((x) => x.setData(expectedData), Moq.Times.once());
}
public async setData_Should_AddLocalHash_When_Called_With_Data(): Promise<void> {
// given
const expectedData = {
first_name: 'SomeName',
};
const expectedHash = 'someHash';
this.studentAPIClientMock
.setup((x) => x.setData(expectedData))
.returns(async () => expectedHash);
// when
await this.testInstance.setData(expectedData);
// then
assert.equal(this.testInstance['localStateHashes'][0], expectedHash);
}
public async getData_Should_Return_Data_From_LocalClient_When_All_Fields_Are_Cached(): Promise<void> {
// given
const providedStudentFields = [StudentField.FIRST_NAME, StudentField.LAST_NAME];
const providedLocalData = {[StudentField.FIRST_NAME]: 'myName', [StudentField.LAST_NAME]: undefined};
this.localStudentClientMock
.setup((x) => x.getData(Moq.It.isAny()))
.returns(async () => providedLocalData);
// when
const result = await this.testInstance.getData(providedStudentFields);
// then
assert.equal(result, providedLocalData);
}
public async getData_Should_Only_Request_Fields_When_Fields_Are_Not_Cached(): Promise<void> {
// given
const providedStudentFields = [StudentField.FIRST_NAME, StudentField.LAST_NAME];
const providedLocalData = {[StudentField.LAST_NAME]: undefined};
this.localStudentClientMock
.setup((x) => x.getData(Moq.It.isAny()))
.returns(async () => providedLocalData);
// when
const result = await this.testInstance.getData(providedStudentFields);
// then
this.studentAPIClientMock.verify((x) => x.getData([StudentField.FIRST_NAME]), Moq.Times.once());
}
public async getData_Should_Merge_Data_From_Local_And_Remote_When_Fields_Are_Both_Cached_And_Not_Cached(): Promise<void> {
// given
const providedStudentFields = [StudentField.FIRST_NAME, StudentField.LAST_NAME];
const providedLocalData = {[StudentField.LAST_NAME]: undefined};
const providedRemoteData = {[StudentField.FIRST_NAME]: 'MyFirstName'};
this.localStudentClientMock
.setup((x) => x.getData(Moq.It.isAny()))
.returns(async () => providedLocalData);
this.studentAPIClientMock
.setup((x) => x.getData(Moq.It.isAny()))
.returns(async () => providedRemoteData);
const expectedData = {...providedLocalData, ...providedRemoteData};
// when
const result = await this.testInstance.getData(providedStudentFields);
// then
assert.deepEqual(result, expectedData);
}
}