Select Git revision
test_validation.py
-
Thorge Petersen authoredThorge Petersen authored
resource.service.spec.ts 18.04 KiB
import { Mock, mock, useFromMock } from '@alfa-client/test-utils';
import { Observable, lastValueFrom, of, throwError } from 'rxjs';
import { ResourceService } from './resource.service';
import { ResourceRepository } from './resource.repository';
import { LinkRelationName, ResourceServiceConfig, SaveResourceData } from './resource.model';
import { Resource, getUrl } from '@ngxp/rest';
import { createDummyResource } from 'libs/tech-shared/test/resource';
import {
StateResource,
createEmptyStateResource,
createErrorStateResource,
createStateResource,
} from './resource.util';
import { fakeAsync, tick } from '@angular/core/testing';
import { singleCold, singleHot } from './marbles';
import { HttpErrorResponse } from '@angular/common/http';
import { createProblemDetail } from 'libs/tech-shared/test/error';
import { HttpError, ProblemDetail } from '../tech.model';
import { cold } from 'jest-marbles';
import * as ResourceUtil from './resource.util';
describe('ResourceService', () => {
let service: ResourceService<Resource, Resource>;
let config: ResourceServiceConfig<Resource>;
let repository: Mock<ResourceRepository>;
const configResource: Resource = createDummyResource();
const configStateResource: StateResource<Resource> = createStateResource(configResource);
const configStateResource$: Observable<StateResource<Resource>> = of(configStateResource);
const deleteLinkRel: string = 'dummyDeleteLinkRel';
const editLinkRel: string = 'dummyEditLinkRel';
const getLinkRel: LinkRelationName = 'dummyGetLinkRel';
beforeEach(() => {
config = {
resource: configStateResource$,
getLinkRel,
deleteLinkRel,
editLinkRel,
};
repository = mock(ResourceRepository);
service = new ResourceService(config, useFromMock(repository));
});
it('should be created', () => {
expect(service).toBeTruthy();
});
describe('get', () => {
const stateResource: StateResource<Resource> = createStateResource(configResource);
beforeEach(() => {
service.stateResource.next(stateResource);
service.handleNullConfigResource = jest.fn();
service.handleConfigResource = jest.fn();
service.handleConfigResourceChanged = jest.fn();
service.shouldFilter = jest.fn().mockReturnValue(true);
});
it('should handle config resource changed', fakeAsync(() => {
service.get().subscribe();
tick();
expect(service.handleConfigResourceChanged).toHaveBeenCalledWith(configResource);
}));
it('should handle null configresource', fakeAsync(() => {
service.get().subscribe();
tick();
expect(service.handleNullConfigResource).toHaveBeenCalledWith(configResource);
}));
it('should handle configresource', fakeAsync(() => {
service.get().subscribe();
tick();
expect(service.handleConfigResource).toHaveBeenCalledWith(stateResource, configResource);
}));
it('should call shouldFilter', fakeAsync(() => {
service.get().subscribe();
tick();
expect(service.shouldFilter).toHaveBeenCalled();
}));
it('should return initial value', () => {
service.stateResource.asObservable = jest
.fn()
.mockReturnValue(singleHot(stateResource, '-a'));
const apiRootStateResource$: Observable<StateResource<Resource>> = service.get();
expect(apiRootStateResource$).toBeObservable(
cold('a', { a: createEmptyStateResource(true) }),
);
});
});
describe('handle config resource changed', () => {
const changedConfigResource: Resource = createDummyResource();
it('should update configresource if is different', () => {
service.configResource = createDummyResource();
service.handleConfigResourceChanged(changedConfigResource);
expect(service.configResource).toBe(changedConfigResource);
});
it('should set stateresource reload', () => {
service.configResource = createDummyResource();
service.handleConfigResourceChanged(changedConfigResource);
expect(service.stateResource.value.reload).toBeTruthy();
});
});
describe('handle null config resource', () => {
const resource: Resource = createDummyResource();
const stateResource: StateResource<Resource> = createStateResource(resource);
beforeEach(() => {
service.shouldClearStateResource = jest.fn();
service.stateResource.next(stateResource);
});
it('should call shouldClearStateResource', () => {
service.handleNullConfigResource(null);
expect(service.shouldClearStateResource).toHaveBeenCalledWith(null);
});
it('should clear stateresource if shouldClearStateResource is true', () => {
service.shouldClearStateResource = jest.fn().mockReturnValue(true);
service.handleNullConfigResource(null);
expect(service.stateResource.value).toEqual(createEmptyStateResource());
});
it('should keep stateresource if shouldClearStateResource is false', () => {
service.shouldClearStateResource = jest.fn().mockReturnValue(false);
service.handleNullConfigResource(null);
expect(service.stateResource.value).toBe(stateResource);
});
});
describe('should clear stateresource', () => {
const resource: Resource = createDummyResource();
const stateResource: StateResource<Resource> = createStateResource(resource);
it('should return true on null configresource and filled stateresource', () => {
service.stateResource.next(stateResource);
const shouldClear: boolean = service.shouldClearStateResource(null);
expect(shouldClear).toBeTruthy();
});
it('should return false on null configresource and empty stateresource', () => {
service.stateResource.next(createEmptyStateResource());
const shouldClear: boolean = service.shouldClearStateResource(null);
expect(shouldClear).toBeFalsy();
});
});
describe('handle config resource', () => {
const resource: Resource = createDummyResource();
const stateResource: StateResource<Resource> = createStateResource(resource);
it('should call shouldLoadResource', () => {
service.shouldLoadResource = jest.fn();
service.handleConfigResource(stateResource, configResource);
expect(service.shouldLoadResource).toHaveBeenCalledWith(stateResource, configResource);
});
it('should load resource', () => {
service.shouldLoadResource = jest.fn().mockReturnValue(true);
service.loadResource = jest.fn();
service.handleConfigResource(stateResource, configResource);
expect(service.loadResource).toHaveBeenCalledWith(configResource);
});
it('should NOT load resource on shouldLoadResource false', () => {
service.loadResource = jest.fn();
service.shouldLoadResource = jest.fn().mockReturnValue(false);
service.handleConfigResource(stateResource, configResource);
expect(service.loadResource).not.toHaveBeenCalled();
});
});
describe('should load resource', () => {
const resource: Resource = createDummyResource();
const stateResource: StateResource<Resource> = createStateResource(resource);
let isLoadingRequiredSpy: jest.SpyInstance<boolean>;
beforeEach(() => {
isLoadingRequiredSpy = jest.spyOn(ResourceUtil, 'isLoadingRequired');
});
it('should return true on existing configresource and loading is required', () => {
isLoadingRequiredSpy.mockReturnValue(true);
const shouldLoad: boolean = service.shouldLoadResource(stateResource, configResource);
expect(shouldLoad).toBeTruthy();
});
it('should call isLoadingRequired', () => {
service.shouldLoadResource(stateResource, configResource);
expect(isLoadingRequiredSpy).toBeCalledWith(stateResource);
});
it('should return false if configresource exists but loading is NOT required', () => {
isLoadingRequiredSpy.mockReturnValue(false);
const shouldLoad: boolean = service.shouldLoadResource(stateResource, configResource);
expect(shouldLoad).toBeFalsy();
});
});
describe('load resource', () => {
const configResourceWithGetLinkRel: Resource = createDummyResource([getLinkRel]);
it('should throw error if getLinkRel not exists', () => {
expect(() => service.loadResource(configResource)).toThrowError(
'No get link exists on configresource.',
);
});
it('should set stateResource loading', () => {
service.doLoadResource = jest.fn();
service.setStateResourceLoading = jest.fn();
service.loadResource(configResourceWithGetLinkRel);
expect(service.setStateResourceLoading).toHaveBeenCalled();
});
it('should call do load resource', () => {
service.doLoadResource = jest.fn();
service.loadResource(configResourceWithGetLinkRel);
expect(service.doLoadResource).toHaveBeenCalledWith(configResourceWithGetLinkRel);
});
});
describe('set state resource loading', () => {
it('should set loading true', () => {
service.stateResource.next(createStateResource(createDummyResource()));
service.setStateResourceLoading();
expect(service.stateResource.value.loading).toBeTruthy();
});
it('should set reload false', () => {
service.stateResource.next({ ...createStateResource(createDummyResource()), reload: true });
service.setStateResourceLoading();
expect(service.stateResource.value.reload).toBeFalsy();
});
});
describe('do load resource', () => {
const configResourceWithGetLinkRel: Resource = createDummyResource([getLinkRel]);
const loadedResource: Resource = createDummyResource();
beforeEach(() => {
repository.getResource.mockReturnValue(of(loadedResource));
});
it('should call repository', () => {
service.doLoadResource(configResourceWithGetLinkRel);
expect(repository.getResource).toHaveBeenCalledWith(
getUrl(configResourceWithGetLinkRel, getLinkRel),
);
});
it('should update stateresource', () => {
service.updateStateResource = jest.fn();
service.doLoadResource(configResourceWithGetLinkRel);
expect(service.updateStateResource).toHaveBeenCalledWith(loadedResource);
});
});
describe('update stateresource', () => {
const resourceToBeSet: Resource = createDummyResource();
it('should set resource as stateresource', () => {
service.stateResource.next(createEmptyStateResource());
service.updateStateResource(resourceToBeSet);
expect(service.stateResource.value).toEqual(createStateResource(resourceToBeSet));
});
});
describe('should filter', () => {
it('should return true if stateresource reload is true', () => {
const shouldFilter: boolean = service.shouldFilter({
...createStateResource(createDummyResource()),
reload: true,
});
expect(shouldFilter).toBeTruthy();
});
it('should return true on invalidCombination', () => {
service.isInvalidResourceCombination = jest.fn().mockReturnValue(true);
const shouldFilter: boolean = service.shouldFilter(
createStateResource(createDummyResource()),
);
expect(shouldFilter).toBeTruthy();
});
it('should call isInvalidCombination', () => {
const stateResource: StateResource<Resource> = createStateResource(createDummyResource());
service.configResource = configResource;
service.isInvalidResourceCombination = jest.fn();
service.shouldFilter(stateResource);
expect(service.isInvalidResourceCombination).toHaveBeenCalledWith(
stateResource,
configResource,
);
});
});
describe('is invalid resource combination', () => {
it('should return true on loaded stateResource while configResource is null', () => {
const stateResource: StateResource<Resource> = createStateResource(createDummyResource());
const isInvalidCombination: boolean = service.isInvalidResourceCombination(
stateResource,
null,
);
expect(isInvalidCombination).toBeTruthy();
});
it('should return true on non loaded stateResource while configResource exists', () => {
const stateResource: StateResource<Resource> = createEmptyStateResource();
const configResource: Resource = createDummyResource();
const isInvalidCombination: boolean = service.isInvalidResourceCombination(
stateResource,
configResource,
);
expect(isInvalidCombination).toBeTruthy();
});
it('should return false on loading stateResource', () => {
const stateResource: StateResource<Resource> = {
...createStateResource(createDummyResource()),
loading: true,
};
const configResource: Resource = createDummyResource();
const isInvalidCombination: boolean = service.isInvalidResourceCombination(
stateResource,
configResource,
);
expect(isInvalidCombination).toBeFalsy();
});
});
describe('save', () => {
const dummyToSave: unknown = {};
const loadedResource: Resource = createDummyResource();
const resourceWithEditLinkRel: Resource = createDummyResource([editLinkRel]);
it('should throw error if edit link not exists', () => {
service.stateResource.next(createStateResource(createDummyResource()));
expect(() => service.save(dummyToSave)).toThrowError(
'No edit link exists on current stateresource.',
);
});
it('should call repository', fakeAsync(() => {
service.stateResource.next(createStateResource(resourceWithEditLinkRel));
repository.save.mockReturnValue(of(loadedResource));
service.save(dummyToSave).subscribe();
tick();
const expectedSaveResourceData: SaveResourceData<Resource> = {
resource: resourceWithEditLinkRel,
linkRel: editLinkRel,
toSave: dummyToSave,
};
expect(repository.save).toHaveBeenCalledWith(expectedSaveResourceData);
}));
it('should return saved object', () => {
service.stateResource.next(createStateResource(resourceWithEditLinkRel));
repository.save.mockReturnValue(singleHot(loadedResource));
const saved: Observable<StateResource<Resource | HttpError>> = service.save(dummyToSave);
expect(saved).toBeObservable(singleCold(createStateResource(loadedResource)));
});
it('should call handleError', () => {
service.stateResource.next(createStateResource(createDummyResource([config.editLinkRel])));
const errorResponse: ProblemDetail = createProblemDetail();
repository.save.mockReturnValue(throwError(() => errorResponse));
service.handleError = jest.fn();
service.save(<any>{}).subscribe();
expect(service.handleError).toHaveBeenCalledWith(errorResponse);
});
});
describe('handleError', () => {
it('should return error stateresource on problem unprocessable entity', (done) => {
const error: ProblemDetail = createProblemDetail();
service
.handleError(<HttpErrorResponse>(<any>error))
.subscribe((responseError: StateResource<HttpError>) => {
expect(responseError).toEqual(createErrorStateResource(error));
done();
});
});
it('should rethrow error', () => {
const error: HttpErrorResponse = <HttpErrorResponse>{
status: 500,
statusText: 'Internal Server Error',
};
const thrownError$: Observable<StateResource<HttpError>> = service.handleError(error);
expect.assertions(1);
expect(lastValueFrom(thrownError$)).rejects.toThrowError('Internal Server Error');
});
});
describe('refresh', () => {
beforeEach(() => {
service.loadResource = jest.fn();
});
it('should set reload true on statresource', () => {
service.stateResource.next(createStateResource(createDummyResource()));
service.refresh();
expect(service.stateResource.value.reload).toBeTruthy();
});
});
describe('can edit', () => {
it('should return true if link is present', () => {
const resource: StateResource<Resource> = createStateResource(
createDummyResource([editLinkRel]),
);
service.stateResource.next(resource);
const canEdit: boolean = service.canEdit();
expect(canEdit).toBeTruthy();
});
it('should return false if link is NOT present', () => {
const resource: StateResource<Resource> = createStateResource(createDummyResource());
service.stateResource.next(resource);
const canEdit: boolean = service.canEdit();
expect(canEdit).toBeFalsy();
});
});
describe('can delete', () => {
it('should return true if link is present', () => {
const resource: StateResource<Resource> = createStateResource(
createDummyResource([deleteLinkRel]),
);
service.stateResource.next(resource);
const canEdit: boolean = service.canDelete();
expect(canEdit).toBeTruthy();
});
it('should return false if link is NOT present', () => {
const resource: StateResource<Resource> = createStateResource(createDummyResource());
service.stateResource.next(resource);
const canEdit: boolean = service.canDelete();
expect(canEdit).toBeFalsy();
});
});
describe('delete', () => {
const resourceWithDeleteLinkRel: Resource = createDummyResource([deleteLinkRel]);
const stateResourceWithDeleteLink: StateResource<Resource> =
createStateResource(resourceWithDeleteLinkRel);
beforeEach(() => {
service.stateResource.next(stateResourceWithDeleteLink);
});
it('should throw error if delete linkRel not exists on current stateresource', () => {
service.stateResource.next(createStateResource(createDummyResource()));
expect(() => service.delete()).toThrowError(
'No delete link exists on current stateresource.',
);
});
it('should call repository', () => {
service.delete();
expect(repository.delete).toHaveBeenCalledWith(resourceWithDeleteLinkRel, deleteLinkRel);
});
it('should return value', () => {
const deleteResource: Resource = createDummyResource();
repository.delete.mockReturnValue(singleHot(deleteResource));
const deletedResource: Observable<Resource> = service.delete();
expect(deletedResource).toBeObservable(singleCold(deleteResource));
});
});
});