Skip to content

Commit

Permalink
N21-2103 cleanup, add missing unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
GordonNicholasCap committed Dec 19, 2024
1 parent 317ef9a commit 260d280
Show file tree
Hide file tree
Showing 20 changed files with 244 additions and 138 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { SchoolForSchoolMediaLicenseSyncNotFoundLoggable } from './school-for-school-media-license-sync-not-found.loggable';

describe(SchoolForSchoolMediaLicenseSyncNotFoundLoggable.name, () => {
describe('getLogMessage', () => {
const setup = () => {
const officialSchoolNumber = '00100';
const exception = new SchoolForSchoolMediaLicenseSyncNotFoundLoggable(officialSchoolNumber);

return { exception, officialSchoolNumber };
};

it('should return the correct log message', () => {
const { exception, officialSchoolNumber } = setup();

const logMessage = exception.getLogMessage();

expect(logMessage).toEqual({
message: 'Unable to sync media school license, because school cannot be found.',
data: {
officialSchoolNumber,
},
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { ErrorLogMessage, Loggable, LogMessage, ValidationErrorLogMessage } from '@src/core/logger';

export class SchoolForSchoolMediaLicenseSyncNotFoundLoggable implements Loggable {
constructor(private readonly schoolNumber: string) {}
constructor(private readonly officialSchoolNumber: string) {}

// TODO: test and think if this should be in school module
getLogMessage(): LogMessage | ErrorLogMessage | ValidationErrorLogMessage {
return {
message: 'Unable to sync media school license, because school cannot be found.',
data: {
schoolNumber: this.schoolNumber,
officialSchoolNumber: this.officialSchoolNumber,
},
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { VidisResponse } from './vidis.response';
export { VidisItemResponse } from './vidis-item.response';
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { mediaSourceFactory } from '@modules/media-source/testing';
import { MediaSchoolLicenseService } from '@modules/school-license/service/media-school-license.service';
import { MediaSchoolLicense, SchoolLicenseType } from '@modules/school-license';
import { mediaSchoolLicenseFactory } from '@modules/school-license/testing';
import { SchoolForSchoolMediaLicenseSyncNotFoundLoggable } from '@modules/school-license/loggable';
import { SchoolForSchoolMediaLicenseSyncNotFoundLoggable } from '@infra/sync/media-licenses/loggable';
import { School, SchoolService } from '@modules/school';
import { schoolFactory } from '@modules/school/testing';
import { axiosErrorFactory, axiosResponseFactory } from '@shared/testing';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
MediaSourceBasicAuthConfigNotFoundLoggableException,
} from '@modules/media-source/loggable';
import { MediaSchoolLicenseService, MediaSchoolLicense, SchoolLicenseType } from '@modules/school-license';
import { SchoolForSchoolMediaLicenseSyncNotFoundLoggable } from '@modules/school-license/loggable';
import { School, SchoolService } from '@modules/school';
import { DefaultEncryptionService, EncryptionService } from '@infra/encryption';
import { Logger } from '@src/core/logger';
Expand All @@ -14,8 +13,8 @@ import { HttpService } from '@nestjs/axios';
import { Inject, Injectable } from '@nestjs/common';
import { AxiosResponse, isAxiosError } from 'axios';
import { lastValueFrom, Observable } from 'rxjs';
import { VidisResponse } from '../response';
import { VidisItemResponse } from '../response/vidis-item.response';
import { VidisResponse, VidisItemResponse } from '../response';
import { SchoolForSchoolMediaLicenseSyncNotFoundLoggable } from '../loggable';

@Injectable()
export class VidisSyncService {
Expand Down Expand Up @@ -95,27 +94,6 @@ export class VidisSyncService {
await Promise.all(syncItemPromises);
}

private async removeNoLongerAvailableLicenses(
existingLicenses: MediaSchoolLicense[],
schoolNumbersFromVidis: string[]
): Promise<void> {
const vidisSchoolNumberSet = new Set(schoolNumbersFromVidis);

const removalPromises: Promise<void>[] = existingLicenses.map(async (license: MediaSchoolLicense) => {
const school = await this.schoolService.getSchoolById(license.schoolId);
if (!school.officialSchoolNumber) {
return;
}

const isLicenseNoLongerInVidis = !vidisSchoolNumberSet.has(school.officialSchoolNumber);
if (isLicenseNoLongerInVidis) {
await this.mediaSchoolLicenseService.deleteSchoolLicense(license);
}
});

await Promise.all(removalPromises);
}

private async getRequest<T>(url: URL, username: string, password: string): Promise<T> {
const encodedCredentials = btoa(`${username}:${password}`);
const observable: Observable<AxiosResponse<T>> = this.httpService.get(url.toString(), {
Expand All @@ -140,4 +118,25 @@ export class VidisSyncService {
private removePrefix(input: string): string {
return input.replace(/^.*?(\d{5})$/, '$1');
}

private async removeNoLongerAvailableLicenses(
existingLicenses: MediaSchoolLicense[],
schoolNumbersFromVidis: string[]
): Promise<void> {
const vidisSchoolNumberSet = new Set(schoolNumbersFromVidis);

const removalPromises: Promise<void>[] = existingLicenses.map(async (license: MediaSchoolLicense) => {
const school = await this.schoolService.getSchoolById(license.schoolId);
if (!school.officialSchoolNumber) {
return;
}

const isLicenseNoLongerInVidis = !vidisSchoolNumberSet.has(school.officialSchoolNumber);
if (isLicenseNoLongerInVidis) {
await this.mediaSchoolLicenseService.deleteSchoolLicense(license);
}
});

await Promise.all(removalPromises);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Factory } from 'fishery';
import { VidisItemResponse } from '@infra/sync/media-licenses/response/vidis-item.response';
import { VidisItemResponse } from '../response';

export const vidisItemResponseFactory = Factory.define<VidisItemResponse>(({ sequence }) => {
return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,76 +1,139 @@
import { ObjectId } from '@mikro-orm/mongodb';
import { setupEntities } from '@shared/testing';
import { MediaSourceOauthConfig } from '../domain/media-source-oauth-config';
import { MediaSourceOauthConfigEmbeddable } from '../entity/media-source-oauth-config.embeddable';
import { mediaSourceOAuthConfigEmbeddableFactory } from '../testing/media-source-oauth-config.embeddable.factory';
import { mediaSourceOauthConfigFactory } from '../testing/media-source-oauth-config.factory';
import { MediaSourceBasicAuthConfig, MediaSourceOauthConfig } from '../domain';
import { MediaSourceBasicAuthConfigEmbeddable, MediaSourceOauthConfigEmbeddable } from '../entity';
import {
mediaSourceBasicAuthConfigFactory,
mediaSourceOauthConfigFactory,
mediaSourceBasicConfigEmbeddableFactory,
mediaSourceOAuthConfigEmbeddableFactory,
} from '../testing';
import { MediaSourceConfigMapper } from './media-source-config.mapper';

describe('MediaSourceConfigMapper', () => {
describe('mapToDo', () => {
describe('when entity is passed', () => {
const setup = async () => {
await setupEntities();
describe('mapOauthConfigToEmbeddable', () => {
describe('when an oauth config domain object is passed', () => {
const setup = () => {
const configDo = mediaSourceOauthConfigFactory.build();
const expected = new MediaSourceOauthConfigEmbeddable({
_id: new ObjectId(configDo.id),
clientId: configDo.getProps().clientId,
clientSecret: configDo.getProps().clientSecret,
authEndpoint: configDo.getProps().authEndpoint,
method: configDo.getProps().method,
});

return { configDo, expected };
};

it('should return an instance of config embeddable', () => {
const { configDo } = setup();

const result = MediaSourceConfigMapper.mapOauthConfigToEmbeddable(configDo);

expect(result).toBeInstanceOf(MediaSourceOauthConfigEmbeddable);
});

it('should return an embeddable with all properties', () => {
const { configDo, expected } = setup();

const result = MediaSourceConfigMapper.mapOauthConfigToEmbeddable(configDo);

expect(result).toEqual(expected);
});
});
});

describe('mapBasicAuthConfigToEmbeddable', () => {
describe('when a basic auth config domain object is passed', () => {
const setup = () => {
const domainObject = mediaSourceBasicAuthConfigFactory.build();
const expected = new MediaSourceBasicAuthConfigEmbeddable({
_id: new ObjectId(domainObject.id),
username: domainObject.username,
password: domainObject.password,
authEndpoint: domainObject.authEndpoint,
});

return { domainObject, expected };
};

it('should return an instance of config embeddable', () => {
const { domainObject } = setup();

const result = MediaSourceConfigMapper.mapBasicAuthConfigToEmbeddable(domainObject);

expect(result).toBeInstanceOf(MediaSourceBasicAuthConfigEmbeddable);
});

it('should return an embeddable with all properties', () => {
const { domainObject, expected } = setup();

const result = MediaSourceConfigMapper.mapBasicAuthConfigToEmbeddable(domainObject);

expect(result).toEqual(expected);
});
});
});

const entity = mediaSourceOAuthConfigEmbeddableFactory.build();
describe('mapOauthConfigToDo', () => {
describe('when an oauth config embeddable is passed', () => {
const setup = () => {
const embeddable = mediaSourceOAuthConfigEmbeddableFactory.build();
const expected = new MediaSourceOauthConfig({
id: entity._id.toHexString(),
clientId: entity.clientId,
clientSecret: entity.clientSecret,
authEndpoint: entity.authEndpoint,
method: entity.method,
id: embeddable._id.toHexString(),
clientId: embeddable.clientId,
clientSecret: embeddable.clientSecret,
authEndpoint: embeddable.authEndpoint,
method: embeddable.method,
});

return { entity, expected };
return { embeddable, expected };
};

it('should return an instance of config', async () => {
const { entity } = await setup();
it('should return an instance of config', () => {
const { embeddable } = setup();

const result = MediaSourceConfigMapper.mapOauthConfigToDo(entity);
const result = MediaSourceConfigMapper.mapOauthConfigToDo(embeddable);

expect(result).toBeInstanceOf(MediaSourceOauthConfig);
});

it('should return a do with all properties', async () => {
const { entity, expected } = await setup();
it('should return a domain object with all properties', () => {
const { embeddable, expected } = setup();

const result = MediaSourceConfigMapper.mapOauthConfigToDo(entity);
const result = MediaSourceConfigMapper.mapOauthConfigToDo(embeddable);

expect(result).toEqual(expected);
});
});
});

describe('mapToEntity', () => {
describe('when config do is passed', () => {
const setup = async () => {
await setupEntities();

const configDo = mediaSourceOauthConfigFactory.build();
const expected = new MediaSourceOauthConfigEmbeddable({
_id: new ObjectId(configDo.id),
clientId: configDo.getProps().clientId,
clientSecret: configDo.getProps().clientSecret,
authEndpoint: configDo.getProps().authEndpoint,
method: configDo.getProps().method,
describe('mapBasicAuthConfigToDo', () => {
describe('when a basic auth config embeddable is passed', () => {
const setup = () => {
const embeddable = mediaSourceBasicConfigEmbeddableFactory.build();
const expected = new MediaSourceBasicAuthConfig({
id: embeddable._id.toHexString(),
username: embeddable.username,
password: embeddable.password,
authEndpoint: embeddable.authEndpoint,
});

return { configDo, expected };
return { embeddable, expected };
};

it('should return an instance of config embeddable', async () => {
const { configDo } = await setup();
it('should return an instance of config', () => {
const { embeddable } = setup();

const result = MediaSourceConfigMapper.mapOauthConfigToEntity(configDo);
const result = MediaSourceConfigMapper.mapBasicAuthConfigToDo(embeddable);

expect(result).toBeInstanceOf(MediaSourceOauthConfigEmbeddable);
expect(result).toBeInstanceOf(MediaSourceBasicAuthConfig);
});

it('should return an embeddable with all properties', async () => {
const { configDo, expected } = await setup();
it('should return a domain object with all properties', () => {
const { embeddable, expected } = setup();

const result = MediaSourceConfigMapper.mapOauthConfigToEntity(configDo);
const result = MediaSourceConfigMapper.mapBasicAuthConfigToDo(embeddable);

expect(result).toEqual(expected);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { MediaSourceBasicAuthConfig, MediaSourceOauthConfig } from '../domain';
import { MediaSourceBasicAuthConfigEmbeddable, MediaSourceOauthConfigEmbeddable } from '../entity';

export class MediaSourceConfigMapper {
static mapOauthConfigToEntity(config: MediaSourceOauthConfig): MediaSourceOauthConfigEmbeddable {
static mapOauthConfigToEmbeddable(config: MediaSourceOauthConfig): MediaSourceOauthConfigEmbeddable {
const configProps = config.getProps();

const configEmbeddable = new MediaSourceOauthConfigEmbeddable({
Expand All @@ -17,7 +17,7 @@ export class MediaSourceConfigMapper {
return configEmbeddable;
}

static mapBasicConfigToEntity(config: MediaSourceBasicAuthConfig): MediaSourceBasicAuthConfigEmbeddable {
static mapBasicAuthConfigToEmbeddable(config: MediaSourceBasicAuthConfig): MediaSourceBasicAuthConfigEmbeddable {
const configProps = config.getProps();

const configEmbeddable = new MediaSourceBasicAuthConfigEmbeddable({
Expand All @@ -42,7 +42,7 @@ export class MediaSourceConfigMapper {
return config;
}

static mapBasicConfigToDo(embeddable: MediaSourceBasicAuthConfigEmbeddable): MediaSourceBasicAuthConfig {
static mapBasicAuthConfigToDo(embeddable: MediaSourceBasicAuthConfigEmbeddable): MediaSourceBasicAuthConfig {
const config = new MediaSourceBasicAuthConfig({
id: embeddable._id.toHexString(),
username: embeddable.username,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ export class MediaSourceMapper {
name: entityDO.name,
sourceId: entityDO.sourceId,
oauthConfig: entityDO.oauthConfig
? MediaSourceConfigMapper.mapOauthConfigToEntity(entityDO.oauthConfig)
? MediaSourceConfigMapper.mapOauthConfigToEmbeddable(entityDO.oauthConfig)
: undefined,
basicAuthConfig: entityDO.basicAuthConfig
? MediaSourceConfigMapper.mapBasicConfigToEntity(entityDO.basicAuthConfig)
? MediaSourceConfigMapper.mapBasicAuthConfigToEmbeddable(entityDO.basicAuthConfig)
: undefined,
format: entityDO.format,
};
Expand All @@ -27,7 +27,7 @@ export class MediaSourceMapper {
sourceId: entity.sourceId,
oauthConfig: entity.oauthConfig ? MediaSourceConfigMapper.mapOauthConfigToDo(entity.oauthConfig) : undefined,
basicAuthConfig: entity.basicAuthConfig
? MediaSourceConfigMapper.mapBasicConfigToDo(entity.basicAuthConfig)
? MediaSourceConfigMapper.mapBasicAuthConfigToDo(entity.basicAuthConfig)
: undefined,
format: entity.format,
});
Expand Down
Loading

0 comments on commit 260d280

Please sign in to comment.