@spotinst/spinnaker-deck
Version:
Spinnaker-Deck service, forked with support to Spotinst
455 lines (359 loc) • 15.6 kB
text/typescript
import Spy = jasmine.Spy;
import { mock, noop } from 'angular';
import { AuthenticationInitializer } from '../authentication/AuthenticationInitializer';
import { ICache } from '../cache';
import { API, InvalidAPIResponse, invalidContentMessage } from './ApiService';
import { SETTINGS } from 'core/config/settings';
describe('API Service', function () {
let $httpBackend: ng.IHttpBackendService;
let baseUrl: string;
beforeEach(
mock.inject(function (_$httpBackend_: ng.IHttpBackendService) {
$httpBackend = _$httpBackend_;
baseUrl = API.baseUrl;
}),
);
afterEach(function () {
SETTINGS.resetToOriginal();
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
describe('ensure api requests generate normalized urls', function () {
let expected: any;
beforeEach(() => {
expected = {
url: '',
};
});
it('trims leading slashes from urls', function () {
const result = API.one('/foo');
expected.url = `foo`;
expect(result.config).toEqual(jasmine.objectContaining(expected));
});
it('trims repeated leading slashes from urls', function () {
const result = API.one('/////foo');
expected.url = `foo`;
expect(result.config).toEqual(jasmine.objectContaining(expected));
});
it('trims trailing slashes from baseUrl', function () {
SETTINGS.gateUrl = 'http://localhost/';
expect(API.baseUrl).toEqual('http://localhost');
});
it('trims repeated trailing slashes from baseUrl', function () {
SETTINGS.gateUrl = 'http://localhost/////';
expect(API.baseUrl).toEqual('http://localhost');
});
});
describe('validate response content-type header', function () {
it('responses with non-"application/json" content types should trigger a reauthentication request and reject', function () {
spyOn(AuthenticationInitializer, 'reauthenticateUser').and.callFake(noop);
$httpBackend
.expectGET(`${baseUrl}/bad`)
.respond(200, '<html>this is the authentication page</html>', { 'content-type': 'text/html' });
let rejected = false;
API.one('bad')
.get()
.then(noop, () => (rejected = true));
$httpBackend.flush();
expect((AuthenticationInitializer.reauthenticateUser as Spy).calls.count()).toBe(1);
expect(rejected).toBe(true);
});
it('application/foo+json is fine', () => {
spyOn(AuthenticationInitializer, 'reauthenticateUser').and.callFake(noop);
$httpBackend
.expectGET(`${baseUrl}/bad`)
.respond(200, '{"good":"job"}', { 'content-type': 'application/foo+json' });
let rejected = false;
API.one('bad')
.get()
.then(noop, () => (rejected = true));
$httpBackend.flush();
expect((AuthenticationInitializer.reauthenticateUser as Spy).calls.count()).toBe(0);
expect(rejected).toBe(false);
});
it('application/x-yaml;charset=utf-8 is fine, too', () => {
spyOn(AuthenticationInitializer, 'reauthenticateUser').and.callFake(noop);
$httpBackend
.expectGET(`${baseUrl}/yaml`)
.respond(200, '---\nfoo: bar', { 'content-type': 'application/x-yaml;charset=utf-8' });
let rejected = false;
API.one('yaml')
.get()
.then(noop, () => (rejected = true));
$httpBackend.flush();
expect((AuthenticationInitializer.reauthenticateUser as Spy).calls.count()).toBe(0);
expect(rejected).toBe(false);
});
it('string responses starting with <html should trigger a reauthentication request and reject', function () {
spyOn(AuthenticationInitializer, 'reauthenticateUser').and.callFake(noop);
$httpBackend.expectGET(`${baseUrl}/fine`).respond(200, 'this is fine');
let rejected = false;
let succeeded = false;
API.one('fine')
.get()
.then(
() => (succeeded = true),
() => (rejected = true),
);
$httpBackend.flush();
expect((AuthenticationInitializer.reauthenticateUser as Spy).calls.count()).toBe(0);
expect(rejected).toBe(false);
expect(succeeded).toBe(true);
});
it('object and array responses should pass through', function () {
spyOn(AuthenticationInitializer, 'reauthenticateUser').and.callFake(noop);
let rejected = false;
let succeeded = false;
$httpBackend.expectGET(`${baseUrl}/some-array`).respond(200, []);
API.one('some-array')
.get()
.then(
() => (succeeded = true),
() => (rejected = true),
);
$httpBackend.flush();
expect((AuthenticationInitializer.reauthenticateUser as Spy).calls.count()).toBe(0);
expect(rejected).toBe(false);
expect(succeeded).toBe(true);
// verify object responses
rejected = false;
succeeded = false;
$httpBackend.expectGET(`${baseUrl}/some-object`).respond(200, {});
API.one('some-object')
.get()
.then(
() => (succeeded = true),
() => (rejected = true),
);
$httpBackend.flush();
expect((AuthenticationInitializer.reauthenticateUser as Spy).calls.count()).toBe(0);
expect(rejected).toBe(false);
expect(succeeded).toBe(true);
});
it('rejects the request promise with an error when content mismatch occurs', () => {
spyOn(AuthenticationInitializer, 'reauthenticateUser').and.callFake(noop);
$httpBackend
.expectGET(`${baseUrl}/bad`)
.respond(200, '<html>this is the authentication page</html>', { 'content-type': 'text/html' });
let err: any;
API.one('bad')
.get()
.catch((e: any) => (err = e));
$httpBackend.flush();
expect(err instanceof InvalidAPIResponse).toBeTruthy();
});
it('returns a string error message in the format expected by UI components when content mismatch occurs', () => {
spyOn(AuthenticationInitializer, 'reauthenticateUser').and.callFake(noop);
$httpBackend
.expectGET(`${baseUrl}/bad`)
.respond(200, '<html>this is the authentication page</html>', { 'content-type': 'text/html' });
let message = '';
API.one('bad')
.get()
.catch((err: any) => (message = err.data.message));
$httpBackend.flush();
expect(message).toBe(invalidContentMessage);
});
it('returns a copy of the original response when content mismatch occurs', () => {
spyOn(AuthenticationInitializer, 'reauthenticateUser').and.callFake(noop);
const serverResult = { foo: 'bar' };
$httpBackend.expectGET(`${baseUrl}/bad`).respond(200, serverResult, { 'content-type': 'foobar/json' });
let receivedResult = null;
API.one('bad')
.get()
.catch((err: any) => (receivedResult = err.originalResult.data));
$httpBackend.flush();
expect(receivedResult).toEqual(serverResult);
});
});
describe('creating the config and testing the chaining functions without parameters', () => {
let expected: any;
beforeEach(() => {
expected = {
url: '',
};
});
describe('creating the config with "one" function', function () {
// it('missing url should create a default config with the base url', function () {
// const result = API.one();
// expected.url = baseUrl;
// expect(result.config).toEqual(jasmine.objectContaining(expected));
// });
it('single url should create a default config with the base url', function () {
const result = API.one('foo');
expected.url = `foo`;
expect(result.config).toEqual(jasmine.objectContaining(expected));
});
it('multiple calls to "one" should create a default config with the base url and build out the url', function () {
const result = API.one('foo').one('bar');
expected.url = `foo/bar`;
expect(result.config).toEqual(jasmine.objectContaining(expected));
});
it('should allow for multiple urls to be added to the url', function () {
const result = API.one('foo', 'bar');
expected.url = `foo/bar`;
expect(result.config).toEqual(jasmine.objectContaining(expected));
});
});
describe('creating the config with "all" function', function () {
// it('missing url should create a default config with the base url', function () {
// const result = API.all();
// expected.url = '';
// expect(result.config).toEqual(jasmine.objectContaining(expected));
// });
it('single url should create a default config with the base url', function () {
const result = API.all('foo');
expected.url = `foo`;
expect(result.config).toEqual(jasmine.objectContaining(expected));
});
it('multiple calls to "all" should create a default config with the base url and build out the url', function () {
const result = API.all('foo').all('bar');
expected.url = `foo/bar`;
expect(result.config).toEqual(jasmine.objectContaining(expected));
});
it('should allow for multiple urls to be added to the url', function () {
const result = API.all('foo', 'bar');
expected.url = `foo/bar`;
expect(result.config).toEqual(jasmine.objectContaining(expected));
});
});
describe('creating the config with mix of "one" and "all" function', function () {
it('single url should create a default config with the base url', function () {
const result = API.all('foo').one('bar');
expected.url = `foo/bar`;
expect(result.config).toEqual(jasmine.objectContaining(expected));
});
it('multiple calls to "all" should create a default config with the base url and build out the url', function () {
const result = API.one('foo').all('bar');
expected.url = `foo/bar`;
expect(result.config).toEqual(jasmine.objectContaining(expected));
});
it('should allow for multiple urls to be added to the url', function () {
const result = API.all('foo', 'bar').one('baz');
expected.url = `foo/bar/baz`;
expect(result.config).toEqual(jasmine.objectContaining(expected));
});
});
describe('creating multiple endpoints', function () {
it('should not stomp on each other', function () {
const first = API.one('bar');
const second = API.one('foo');
expect(first.config).toEqual(jasmine.objectContaining({ url: `bar` }));
expect(second.config).toEqual(jasmine.objectContaining({ url: `foo` }));
});
});
describe('create config with data', function () {
it('should add data to the config if data object passed', function () {
const data = { bar: 'baz' };
const result = API.one('foo').data(data);
expected.url = `foo`;
expected.data = data;
expect(result.config).toEqual(jasmine.objectContaining(expected));
});
});
});
describe('create a config with params', function () {
it('when params are provided', function () {
const result = API.one('foo').withParams({ one: 1 });
expect(result.config).toEqual(jasmine.objectContaining({ url: `foo`, params: { one: 1 } }));
});
});
describe('useCache()', function () {
it('should set cache to "true" if no params set', function () {
const result = API.one('foo').useCache();
expect(result.config.cache).toBe(true);
});
it('should set cache to "false" if explicitly set', function () {
const result = API.one('foo').useCache(false);
expect(result.config.cache).toBe(false);
});
it('should set cache to cache object if explicitly set', function () {
const cacheObj = ({ count: 1 } as unknown) as ICache;
const result = API.one('foo').useCache(cacheObj);
expect(result.config.cache).toBe(cacheObj as any);
});
});
describe('get(): create a url with a "GET" method', function () {
it('should create the url and issue a get request with the "one" function', function () {
$httpBackend.expectGET(`${baseUrl}/foo`).respond(200);
API.one('foo').get();
$httpBackend.flush();
});
it('should create the url and issue a get request with the "all" function', function () {
$httpBackend.expectGET(`${baseUrl}/foo/bar`).respond(200);
API.all('foo', 'bar').get();
$httpBackend.flush();
});
it('should take a param object with one param', function () {
$httpBackend.expectGET(`${baseUrl}/foo/bar?param1=2`).respond(200);
API.one('foo', 'bar').get({ param1: 2 });
$httpBackend.flush();
});
it('should take a param object with multiple params', function () {
$httpBackend.expectGET(`${baseUrl}/foo/bar?param1=2¶m2=foo`).respond(200);
API.one('foo', 'bar').get({ param1: 2, param2: 'foo' });
$httpBackend.flush();
});
});
describe('getList(): create a url with a "GET" method', function () {
it('should create the url and issue a get request with the "one" function', function () {
$httpBackend.expectGET(`${baseUrl}/foo`).respond(200);
API.one('foo').getList();
$httpBackend.flush();
});
it('should create the url and issue a get request with the "all" function', function () {
$httpBackend.expectGET(`${baseUrl}/foo/bar`).respond(200);
API.all('foo', 'bar').getList();
$httpBackend.flush();
});
it('should take a param object with one param', function () {
$httpBackend.expectGET(`${baseUrl}/foo/bar?param1=2`).respond(200);
API.one('foo', 'bar').getList({ param1: 2 });
$httpBackend.flush();
});
it('should take a param object with multiple params', function () {
$httpBackend.expectGET(`${baseUrl}/foo/bar?param1=2¶m2=foo`).respond(200);
API.one('foo', 'bar').getList({ param1: 2, param2: 'foo' });
$httpBackend.flush();
});
});
describe('post(): create a url with a "POST" method', function () {
it('should create the url and make a POST call', function () {
$httpBackend.expectPOST(`${baseUrl}/foo`).respond(200);
API.one('foo').post();
$httpBackend.flush();
});
it('should create the url and POST with data', function () {
const data = { bar: 7 };
$httpBackend.expectPOST(`${baseUrl}/foo`, data).respond(200);
API.one('foo').post(data);
$httpBackend.flush();
});
});
describe('put(): create a url with a "PUT" method', function () {
it('should create the url and make a POST call', function () {
$httpBackend.expectPUT(`${baseUrl}/foo`).respond(200);
API.one('foo').put();
$httpBackend.flush();
});
it('should create the url and PUT with data', function () {
const data = { bar: 7 };
$httpBackend.expectPUT(`${baseUrl}/foo`, data).respond(200);
API.one('foo').put(data);
$httpBackend.flush();
});
});
describe('remove(): create a url with a "DELETE" method', function () {
it('should create the url and make a DELETE call', function () {
$httpBackend.expectDELETE(`${baseUrl}/foo`).respond(200);
API.one('foo').remove();
$httpBackend.flush();
});
it('should create the url with params and make a DELETE call', function () {
const params = { bar: 7 };
$httpBackend.expectDELETE(`${baseUrl}/foo?bar=7`).respond(200);
API.one('foo').query(params).remove();
$httpBackend.flush();
});
});
});