/*global QUnit*/

sap.ui.define([
	"sap/base/Log",
	"sap/ui/core/mvc/XMLView",
	"sap/ui/core/Core",
	"sap/ui/fl/write/_internal/fieldExtensibility/Access",
	"sap/ui/fl/registry/Settings",
	"sap/ui/fl/Utils",
	"sap/ui/thirdparty/jquery",
	"sap/ui/thirdparty/sinon-4"
], function(
	Log,
	XMLView,
	Core,
	Access,
	Settings,
	Utils,
	jQuery,
	sinon
) {
	"use strict";

	var sandbox = sinon.createSandbox();

	var oBusinessExpectedContextRetrievalResult = {
		BusinessContexts: [{ BusinessContext: "CFD_TSM_BUPA_ADR", BusinessContextDescription: "Description for CFD_TSM_BUPA_ADR"}, { BusinessContext: "CFD_TSM_BUPA", BusinessContextDescription: "Description for CFD_TSM_BUPA"}],
		ServiceName: "someService",
		ServiceVersion: "0001"
	};
	var oBusinessExpectedContextRetrievalResultWithDescriptions = {
		BusinessContexts: [{ BusinessContext: "CFD_TSM_BUPA_ADR", BusinessContextDescription: "Description for CFD_TSM_BUPA_ADR" }, { BusinessContext: "CFD_TSM_BUPA", BusinessContextDescription: "Description for CFD_TSM_BUPA" }],
		ServiceName: "someService",
		ServiceVersion: "0001"
	};

	var oBusinessExpectedContextRetrievalResultWithoutBusinesscontexts = {
		BusinessContexts: [],
		ServiceName: "someService",
		ServiceVersion: "0001"
	};

	var oHttpErrorResponse = {
		error: {
			code: "005056A509B11EE1B9A8FEC11C21578E",
			message: {
				lang: "en",
				value: "Invalid Function Import Parameter"
			},
			innererror: {
				transactionid: "54E429A74593458DE10000000A420908",
				timestamp: "20150219074515.1395610",
				Error_Resolution: {
					SAP_Transaction: "Run transaction /IWFND/ERROR_LOG on SAP NW Gateway hub system and search for entries with the timestamp above for more details", SAP_Note: "See SAP Note 1797736 for error analysis (https://service.sap.com/sap/support/notes/1797736)"
				}
			}
		}
	};

	var oExpectedErrorResult = {
		severity: "error",
		text: "{\"error\":{\"code\":\"005056A509B11EE1B9A8FEC11C21578E\",\"message\":{\"lang\":\"en\",\"value\":\"Invalid Function Import Parameter\"},\"innererror\":{\"transactionid\":\"54E429A74593458DE10000000A420908\",\"timestamp\":\"20150219074515.1395610\",\"Error_Resolution\":{\"SAP_Transaction\":\"Run transaction /IWFND/ERROR_LOG on SAP NW Gateway hub system and search for entries with the timestamp above for more details\",\"SAP_Note\":\"See SAP Note 1797736 for error analysis (https://service.sap.com/sap/support/notes/1797736)\"}}}}"
	};

	var oServiceUri = {
		sServiceName: "SOME_SRVC_NM",
		sNamespace: "/namespace/",
		sPrefix: "/someService",
		sODataPrefix: "/sap/opu/odata",
		sSapPrefix: "/SAP",
		sVersion: ";v=0007"
	};

	var mockAccessJs = function() {
		var oUnchangedAccess = {};
		for (var propertyName in Access) {
			oUnchangedAccess[propertyName] = Access[propertyName];
		}

		Access._getSystemInfo = function() {
			return {
				getName: function() {
					return "ABC";
				},
				getClient: function() {
					return "123";
				}
			};
		};

		Access._isSystemInfoAvailable = function() {
			return true;
		};

		return oUnchangedAccess;
	};

	var unMockAccessJs = function(oUnchangedAccess) {
		for (var propertyName in oUnchangedAccess) {
			Access[propertyName] = oUnchangedAccess[propertyName];
		}
	};

	var checkService = function(assert, sUri, sExpectedServiceName, sExpectedServiceVersion) {
		var oService = Access._parseServiceUri(sUri);
		assert.equal(oService.serviceName, sExpectedServiceName, "ServiceName correct");
		assert.equal(oService.serviceVersion, sExpectedServiceVersion, "ServiceVersion correct");
	};

	var checkServiceName = function(assert, sUri, sExpectedServiceName) {
		var oService = Access._parseServiceUri(sUri);
		assert.equal(oService.serviceName, sExpectedServiceName);
	};

	var checkServiceVersion = function(assert, sUri, sExpectedServiceVersion) {
		var oService = Access._parseServiceUri(sUri);
		assert.equal(oService.serviceVersion, sExpectedServiceVersion);
	};

	QUnit.module("sap.ui.fl.fieldExt.Access", {}, function() {
		QUnit.test("_parseServiceName can extract a service name from an uri without a namespace", function(assert) {
			var sServiceName = oServiceUri.sServiceName;
			var sDeterminedServiceName = Access._parseServiceUri(sServiceName).serviceName;
			assert.equal(sDeterminedServiceName, sServiceName);
		});

		QUnit.test("_parseServiceName can extract a service name from an 'sap/opu/odata/' uri", function(assert) {
			var sServiceNameWithNamespace = oServiceUri.sNamespace + oServiceUri.sServiceName;
			var sServiceUri = oServiceUri.sPrefix + oServiceUri.sODataPrefix + sServiceNameWithNamespace;
			var sDeterminedServiceName = Access._parseServiceUri(sServiceUri).serviceName;
			assert.equal(sDeterminedServiceName, sServiceNameWithNamespace);
		});

		QUnit.test("_parseServiceName can extract a simple service name from an 'sap/opu/odata/' uri with version information", function(assert) {
			var sServiceNameWithNamespace = oServiceUri.sNamespace + oServiceUri.sServiceName;
			var sServiceUri = oServiceUri.sPrefix + oServiceUri.sODataPrefix + sServiceNameWithNamespace + oServiceUri.sVersion;
			var sDeterminedServiceName = Access._parseServiceUri(sServiceUri).serviceName;
			assert.equal(sDeterminedServiceName, sServiceNameWithNamespace);
		});

		QUnit.test("_parseServiceName can extract a service name from an 'sap/opu/odata/SAP/' uri", function(assert) {
			var sServiceName = oServiceUri.sServiceName;
			var sServiceUri = oServiceUri.sPrefix + oServiceUri.sODataPrefix + oServiceUri.sSapPrefix + "/" + sServiceName;
			var sDeterminedServiceName = Access._parseServiceUri(sServiceUri).serviceName;
			assert.equal(sDeterminedServiceName, sServiceName);
		});

		QUnit.test("_parseServiceName can extract a simple service name from an 'sap/opu/odata/SAP/' uri with version information", function(
			assert) {
			var sServiceName = oServiceUri.sServiceName;
			var sServiceUri = oServiceUri.sPrefix + oServiceUri.sODataPrefix + oServiceUri.sSapPrefix + "/" + sServiceName + oServiceUri.sVersion;
			var sDeterminedServiceName = Access._parseServiceUri(sServiceUri).serviceName;
			assert.equal(sDeterminedServiceName, sServiceName);
		});

		QUnit.test("getBusinessContextsByEntityType", function(assert) {
			var sServiceUrl = "/someService";
			var sEntityName = "BusinessPartner";

			var oServer;
			oServer = sinon.fakeServer.create();

			oServer.autoRespond = true;
			try {
				var oPromise = Access.getBusinessContexts(sServiceUrl, sEntityName);

				oPromise.done(function(oBusinessContexts) {
					oServer.restore();
					assert.deepEqual(oBusinessContexts, oBusinessExpectedContextRetrievalResultWithDescriptions);
				});

				oPromise.fail(function(error) {
					oServer.restore();
					assert.ok(false, "Should not run into fail branch. Error" + error);
				});

				oServer.requests[0].respond(200, {
					"Content-Type": "application/json",
					"Content-Length": 13,
					"X-CSRF-Token": "0987654321"
				}, '{ "d": {"results":[{"BusinessContext":"CFD_TSM_BUPA_ADR", "BusinessContextDescription": "Description for CFD_TSM_BUPA_ADR"},{"BusinessContext":"CFD_TSM_BUPA" , "BusinessContextDescription": "Description for CFD_TSM_BUPA"}] }}');
			} catch (e) {
				oServer.restore();
				assert.ok(false, e);
			}
		});

		QUnit.test("getBusinessContextsByEntitySet", function(assert) {
			var sServiceUrl = "/someService";
			var sEntitySetName = "BusinessPartnerSet";

			var oServer;
			oServer = sinon.fakeServer.create();
			oServer.autoRespond = true;
			try {
				var oPromise = Access.getBusinessContexts(sServiceUrl, null, sEntitySetName);

				oPromise.done(function(oBusinessContexts) {
					oServer.restore();
					assert.deepEqual(oBusinessContexts, oBusinessExpectedContextRetrievalResult);
				});

				oPromise.fail(function(error) {
					oServer.restore();
					assert.ok(false, "Should not run into fail branch. Error" + error);
				});

				oServer.requests[0].respond(200, {
					"Content-Type": "application/json",
					"Content-Length": 13,
					"X-CSRF-Token": "0987654321"
				}, '{ "d": {"results":[{"BusinessContext":"CFD_TSM_BUPA_ADR", "BusinessContextDescription": "Description for CFD_TSM_BUPA_ADR"},{"BusinessContext":"CFD_TSM_BUPA", "BusinessContextDescription": "Description for CFD_TSM_BUPA" }] }}');
			} catch (e) {
				oServer.restore();
				assert.ok(false, e);
			}
		});
		QUnit.test("getBusinessContextsByEntitySetWithDescriptions", function(assert) {
			var sServiceUrl = "/someService";
			var sEntitySetName = "BusinessPartnerSet";

			var oServer;
			oServer = sinon.fakeServer.create();
			oServer.autoRespond = true;
			try {
				var oPromise = Access.getBusinessContexts(sServiceUrl, null, sEntitySetName);

				oPromise.done(function(oBusinessContexts) {
					oServer.restore();
					assert.deepEqual(oBusinessContexts, oBusinessExpectedContextRetrievalResultWithDescriptions);
				});

				oPromise.fail(function(error) {
					oServer.restore();
					assert.ok(false, "Should not run into fail branch. Error" + error);
				});

				oServer.requests[0].respond(200, {
					"Content-Type": "application/json",
					"Content-Length": 13,
					"X-CSRF-Token": "0987654321"
				}, '{ "d": {"results":[{"BusinessContext":"CFD_TSM_BUPA_ADR", "BusinessContextDescription":"Description for CFD_TSM_BUPA_ADR"}, { "BusinessContext":"CFD_TSM_BUPA", "BusinessContextDescription":"Description for CFD_TSM_BUPA" }] }}');
			} catch (e) {
				oServer.restore();
				assert.ok(false, e);
			}
		});

		QUnit.test("getBusinessContextsWhereNoContextsAreReturned", function(assert) {
			var sServiceUrl = "/someService";
			var sEntityTypeName = "BusinessPartner";

			var oServer;
			oServer = sinon.fakeServer.create();
			oServer.autoRespond = true;
			try {
				var oPromise = Access.getBusinessContexts(sServiceUrl, sEntityTypeName);

				oPromise.done(function(oBusinessContexts) {
					oServer.restore();
					assert.deepEqual(oBusinessContexts, oBusinessExpectedContextRetrievalResultWithoutBusinesscontexts);
				});

				oPromise.fail(function(error) {
					oServer.restore();
					assert.ok(false, "Should not run into fail branch. Error" + error);
				});

				oServer.requests[0].respond(200, {
					"Content-Type": "application/json",
					"Content-Length": 13,
					"X-CSRF-Token": "0987654321"
				}, '{ "results":[] }');
			} catch (e) {
				oServer.restore();
				assert.ok(false, e);
			}
		});

		//In case EntitySetName was not provided at all - at least EntitySetName='' has to be provided -
		// the real get response is used in respond parameter of this test
		QUnit.test("getBusinessContextsWhereRetrievalFails", function(assert) {
			var sServiceUrl = "/someService";
			var sEntityTypeName = "BusinessPartner";

			var oServer;
			oServer = sinon.fakeServer.create();
			oServer.autoRespond = true;
			try {
				var oPromise = Access.getBusinessContexts(sServiceUrl, sEntityTypeName);

				oPromise.done(function() {
					oServer.restore();
					assert.ok(false, "Should not run into done branch. ");
				});

				oPromise.fail(function(oError) {
					oServer.restore();
					assert.equal(oError.errorMessages[0].text, "Invalid Function Import Parameter");
					assert.equal(oError.errorMessages[0].severity, oExpectedErrorResult.severity);
				});

				oServer.requests[0].respond(404, {
					"Content-Type": "application/json",
					"Content-Length": 13,
					"X-CSRF-Token": "0987654321"
				}, JSON.stringify(oHttpErrorResponse));
			} catch (e) {
				oServer.restore();
				assert.ok(false, e);
			}
		});

		QUnit.test("Test set service invalid", function(assert) {
			var oUnchangedAccess = mockAccessJs();
			var service = {
				serviceName: "abc",
				serviceVersion: "0001"
			};

			Access.setServiceValid(service);
			assert.notOk(Access.isServiceOutdated(service));

			Access.setServiceInvalid(service);
			assert.ok(Access.isServiceOutdated(service));
			Access.setServiceValid(service);
			assert.notOk(Access.isServiceOutdated(service));

			unMockAccessJs(oUnchangedAccess);
		});

		QUnit.test("Test expiration date", function(assert) {
			var oUnchangedAccess = mockAccessJs();
			var service = {
				serviceName: "abc",
				serviceVersion: "0001"
			};

			// Mock current time to 5
			Access._getCurrentTime = function() {
				return 5;
			};

			// Clear storage
			Access.setServiceValid(service);
			assert.notOk(Access.isServiceOutdated(service));

			// Check expiration date 5 + 1 week
			Access.setServiceInvalid(service);
			assert.ok(Access.isServiceOutdated(service));

			Access._getCurrentTime = function() {
				return 5 + (1 * 7 * 24 * 60 * 60 * 1000) - 1;
			};

			assert.ok(Access.isServiceOutdated(service));

			Access._getCurrentTime = function() {
				return 5 + (1 * 7 * 24 * 60 * 60 * 1000);
			};

			assert.notOk(Access.isServiceOutdated(service));

			// Make sure the service has been deleted from the local storage
			assert.notOk(Access._getServiceItem(Access._createServiceItem(service)));

			unMockAccessJs(oUnchangedAccess);
		});

		QUnit.test("Test reinvalidate", function(assert) {
			var oUnchangedAccess = mockAccessJs();
			var service = {
				serviceName: "abc",
				serviceVersion: "0001"
			};

			// Clear storage
			Access.setServiceValid(service);
			assert.notOk(Access.isServiceOutdated(service));

			// Mock current time to 5 and invalidate
			Access._getCurrentTime = function() {
				return 5;
			};
			Access.setServiceInvalid(service);

			// Two weeks have been passed
			Access._getCurrentTime = function() {
				return 5 + (2 * 7 * 24 * 60 * 60 * 1000);
			};

			// The service must be valid, but the entry is still there.
			// Let`s invalidate the service again
			Access.setServiceInvalid(service);

			// Service have to be stale
			assert.ok(Access.isServiceOutdated(service));
			assert.ok(Access.isServiceOutdated(service));
			Access.setServiceValid(service);
			assert.notOk(Access.isServiceOutdated(service));

			unMockAccessJs(oUnchangedAccess);
		});

		QUnit.test("Test logon system not available", function(assert) {
			// The service is always valid
			var service = {
				serviceName: "abc",
				serviceVersion: "0001"
			};
			Access.setServiceValid(service);
			assert.notOk(Access.isServiceOutdated(service));
			Access.setServiceInvalid(service);
			assert.notOk(Access.isServiceOutdated(service));
		});

		QUnit.test("Test ushell not available", function(assert) {
			var shell = null;
			if (sap.ushell) {
				shell = sap.ushell;
				delete sap.ushell;
			}

			// The service is always valid
			var service = {
				serviceName: "abc",
				serviceVersion: "0001"
			};
			Access.setServiceValid(service);
			assert.notOk(Access.isServiceOutdated(service));
			Access.setServiceInvalid(service);
			assert.notOk(Access.isServiceOutdated(service));

			if (shell) {
				sap.ushell = shell;
			}
		});

		QUnit.test("Test uniqueness", function(assert) {
			var oUnchangedAccess = mockAccessJs();
			var service = {
				serviceName: "abc",
				serviceVersion: "0001"
			};

			var serviceModifiedName = {
				serviceName: "abcd",
				serviceVersion: "0001"
			};

			var serviceModifiedVersion = {
				serviceName: "abc",
				serviceVersion: "0002"
			};

			// Clear storage
			Access.setServiceValid(service);
			Access.setServiceValid(serviceModifiedName);
			Access.setServiceValid(serviceModifiedVersion);
			assert.notOk(Access.isServiceOutdated(service));
			assert.notOk(Access.isServiceOutdated(serviceModifiedName));
			assert.notOk(Access.isServiceOutdated(serviceModifiedVersion));

			// Mock current time to 5 and invalidate
			Access._getCurrentTime = function() {
				return 5;
			};
			Access.setServiceInvalid(service);

			assert.ok(Access.isServiceOutdated(service));
			assert.notOk(Access.isServiceOutdated(serviceModifiedName));
			assert.notOk(Access.isServiceOutdated(serviceModifiedVersion));

			unMockAccessJs(oUnchangedAccess);
		});

		QUnit.test("Validate local storage is used", function(assert) {
			var oUnchangedAccess = mockAccessJs();
			var service;
			var storageItem;

			// Run test only if local storage is available
			if (window.localStorage) {
				window.localStorage.setItem("state.key_-sap.ui.fl.fieldExt.Access", "\"{ }\"");

				service = {
					serviceName: "abc",
					serviceVersion: "0001"
				};

				Access.setServiceValid(service);
				assert.notOk(Access.isServiceOutdated(service));
				Access.setServiceInvalid(service);
				assert.ok(Access.isServiceOutdated(service));

				storageItem = window.localStorage.getItem("state.key_-sap.ui.fl.fieldExt.Access");
				assert.ok(storageItem !== "\"{ }\"");
			}
			unMockAccessJs(oUnchangedAccess);
			assert.ok(true);
		});

		QUnit.test("Test no local storage", function(assert) {
			var oUnchangedAccess = mockAccessJs();
			// If no local storage is available => This class does nothing => A service is never outdated
			var service = {
				serviceName: "abc",
				serviceVersion: "0001"
			};

			// We simulate a very old browser
			Access._getLocalStorage = function() {
				return null;
			};

			// Execute tests
			Access.setServiceValid(service);
			assert.notOk(Access.isServiceOutdated(service));

			Access.setServiceInvalid(service);
			assert.notOk(Access.isServiceOutdated(service));

			Access.setServiceValid(service);
			assert.notOk(Access.isServiceOutdated(service));

			unMockAccessJs(oUnchangedAccess);
		});

		QUnit.test("Test set service invalid with relative uri", function(assert) {
			var oUnchangedAccess = mockAccessJs();
			var service = "/sap/opu/odata/SAP/someService";
			var serviceAsObject = {
				serviceName: "someService",
				serviceVersion: "0001"
			};

			Access.setServiceValid(service);
			assert.notOk(Access.isServiceOutdated(service));

			Access.setServiceInvalid(service);
			assert.ok(Access.isServiceOutdated(service));
			Access.setServiceValid(service);
			assert.notOk(Access.isServiceOutdated(service));

			// Different parameters
			Access.setServiceInvalid(service);
			assert.ok(Access.isServiceOutdated(service));
			assert.ok(Access.isServiceOutdated(serviceAsObject));
			Access.setServiceValid(serviceAsObject);
			assert.notOk(Access.isServiceOutdated(service));
			assert.notOk(Access.isServiceOutdated(serviceAsObject));

			unMockAccessJs(oUnchangedAccess);
		});

		QUnit.test("Test set service invalid with relative and version uri", function(assert) {
			var oUnchangedAccess = mockAccessJs();
			var service = "/sap/opu/odata/SAP/someService;v=0002";
			var serviceAsObject = {
				serviceName: "someService",
				serviceVersion: "0002"
			};

			Access.setServiceValid(service);
			assert.notOk(Access.isServiceOutdated(service));

			Access.setServiceInvalid(service);
			assert.ok(Access.isServiceOutdated(service));
			Access.setServiceValid(service);
			assert.notOk(Access.isServiceOutdated(service));

			// Different parameters
			Access.setServiceInvalid(service);
			assert.ok(Access.isServiceOutdated(service));
			assert.ok(Access.isServiceOutdated(serviceAsObject));
			Access.setServiceValid(serviceAsObject);
			assert.notOk(Access.isServiceOutdated(service));
			assert.notOk(Access.isServiceOutdated(serviceAsObject));

			unMockAccessJs(oUnchangedAccess);
		});

		QUnit.test("Parse service version of relative uri", function(assert) {
			checkServiceVersion(assert, "sap/opu/odata/SAP/someServicesomeService;v=0002", "0002");
			checkServiceVersion(assert, "/sap/opu/odata/SAP/someServicesomeService;v=0002/", "0002");
			checkServiceVersion(assert, "sap/opu/odata/ns/someServicesomeService;v=0002", "0002");
			checkServiceVersion(assert, "/sap/opu/odata/ns/someServicesomeService;v=0002/", "0002");
			checkServiceVersion(assert, "/foo/opu/odata/ns/someServicesomeService;v=0002/", "0002");
			checkServiceVersion(assert, "/foo/opu/odata/ns/someServicesomeService;v=0002", "0002");
			checkServiceVersion(assert, "foo/opu/odata/ns/someServicesomeService;v=0002/", "0002");
			checkServiceVersion(assert, "foo/opu/odata/ns/someServicesomeService;v=0002", "0002");
			checkServiceVersion(assert, "foo/opu/odata/ns/someServicesomeService;v=0002;w=foo", "0002");
			checkServiceVersion(assert, "sap/opu/odata/SAP/someServicesomeService", "0001");
			checkServiceVersion(assert, "sap/opu/odata/SAP/someServicesomeService;v=0002;v=0004", "0002");
			checkServiceVersion(assert, "foo/opu/odata/ns/someServicesomeService;v=0002;w=foo?$format=json", "0002");
			checkServiceVersion(assert, "sap/opu/odata/ns/someServicesomeService;v=0002;w=foo?$format=json", "0002");
		});

		QUnit.test("Parse service name (OData v2)", function(assert) {
			// 1.) Case
			checkServiceName(assert, "/sap/opu/odata/SAP/someServicesomeService", "someServicesomeService");
			checkServiceName(assert, "/sap/opu/odata/SAP/someServicesomeService/", "someServicesomeService");
			checkServiceName(assert, "/sap/opu/odata/SAP/someServicesomeService;v=0002", "someServicesomeService");
			checkServiceName(assert, "/sap/opu/odata/SAP/someServicesomeService;v=0002/", "someServicesomeService");

			checkServiceName(assert, "sap/opu/odata/SAP/someServicesomeService", "someServicesomeService");
			checkServiceName(assert, "sap/opu/odata/SAP/someServicesomeService/", "someServicesomeService");
			checkServiceName(assert, "sap/opu/odata/SAP/someServicesomeService;v=0002", "someServicesomeService");
			checkServiceName(assert, "sap/opu/odata/SAP/someServicesomeService;v=0002/", "someServicesomeService");

			checkServiceName(assert, "/sap/opu/odata/sap/someServicesomeService", "someServicesomeService");
			checkServiceName(assert, "/sap/opu/odata/SaP/someServicesomeService/", "someServicesomeService");
			checkServiceName(assert, "/sap/opu/oData/SAP/someServicesomeService;v=0002", "someServicesomeService");
			checkServiceName(assert, "/sap/Opu/odata/SAP/someServicesomeService;v=0002/", "someServicesomeService");
			checkServiceName(assert, "/Sap/Opu/odata/SAP/someServicesomeService;v=0002/", "someServicesomeService");
			checkServiceName(assert, "/Sap/Opu/odata/SAP/someServicesomeService;v=0002/", "someServicesomeService");

			checkServiceName(assert, "sap/opu/odata/SAP/someServicesomeService;v=0002;w=foo?$format=xml", "someServicesomeService");

			// 2.) Case
			checkServiceName(assert, "/sap/opu/odata/PAS/someServicesomeService", "/PAS/someServicesomeService");
			checkServiceName(assert, "/sap/opu/odata/PAS/someServicesomeService/", "/PAS/someServicesomeService");
			checkServiceName(assert, "/sap/opu/odata/PAS/someServicesomeService;v=0002", "/PAS/someServicesomeService");
			checkServiceName(assert, "/sap/opu/odata/PAS/someServicesomeService;v=0002/", "/PAS/someServicesomeService");
			checkServiceName(assert, "sap/opu/odata/PAS/someServicesomeService", "/PAS/someServicesomeService");
			checkServiceName(assert, "sap/opu/odata/PAS/someServicesomeService/", "/PAS/someServicesomeService");
			checkServiceName(assert, "sap/opu/odata/PAS/someServicesomeService;v=0002", "/PAS/someServicesomeService");
			checkServiceName(assert, "sap/opu/odata/PAS/someServicesomeService;v=0002/", "/PAS/someServicesomeService");

			// 3.) Case
			checkServiceName(assert, "/foo/opu/odata/SAP/someServicesomeService", "someServicesomeService");
			checkServiceName(assert, "/foo/opu/odata/SAP/someServicesomeService/", "someServicesomeService");
			checkServiceName(assert, "/foo/opu/odata/SAP/someServicesomeService;v=0002", "someServicesomeService");
			checkServiceName(assert, "/foo/opu/odata/SAP/someServicesomeService;v=0002/", "someServicesomeService");

			checkServiceName(assert, "foo/opu/odata/SAP/someServicesomeService", "someServicesomeService");
			checkServiceName(assert, "foo/opu/odata/SAP/someServicesomeService/", "someServicesomeService");
			checkServiceName(assert, "foo/opu/odata/SAP/someServicesomeService;v=0002", "someServicesomeService");
			checkServiceName(assert, "foo/opu/odata/SAP/someServicesomeService;v=0002/", "someServicesomeService");

			checkServiceName(assert, "/foo/opu/odata/PAS/someServicesomeService", "someServicesomeService");
			checkServiceName(assert, "/foo/opu/odata/PAS/someServicesomeService/", "someServicesomeService");
			checkServiceName(assert, "/foo/opu/odata/PAS/someServicesomeService;v=0002", "someServicesomeService");
			checkServiceName(assert, "/foo/opu/odata/PAS/someServicesomeService;v=0002/", "someServicesomeService");

			checkServiceName(assert, "foo/opu/odata/PAS/someServicesomeService", "someServicesomeService");
			checkServiceName(assert, "foo/opu/odata/PAS/someServicesomeService/", "someServicesomeService");
			checkServiceName(assert, "foo/opu/odata/PAS/someServicesomeService;v=0002", "someServicesomeService");
			checkServiceName(assert, "foo/opu/odata/PAS/someServicesomeService;v=0002/", "someServicesomeService");
		});

		QUnit.test("Parse OData v4 service document (relative, no segment or query parameters)", function(assert) {
			checkService(assert, "sap/opu/odata4/grpNs/grpName/sadl/sap/i_cfd_tsm_so_core/0001", "sadl/sap/i_cfd_tsm_so_core", "0001");
		});

		QUnit.test("Parse OData v4 service document (no segment or query parameters)", function(assert) {
			checkService(assert, "/sap/opu/odata4/grpNs/grpName/sadl/sap/i_cfd_tsm_so_core/0001", "sadl/sap/i_cfd_tsm_so_core", "0001");
			checkService(assert, "/sap/opu/odata4/grpNs/grpName/sadl/sap/i_cfd_tsm_so_core/0002/", "sadl/sap/i_cfd_tsm_so_core", "0002");
		});

		QUnit.test("Parse OData v4 service document (no segment or query parameters) - case sensitive", function(assert) {
			checkService(assert, "/sap/opu/odata4/grpNs/grpName/sadl/sap/I_CFD_TSM_SO_CORE/0001", "sadl/sap/I_CFD_TSM_SO_CORE", "0001");
			checkService(assert, "/sap/opu/odata4/grpNs/grpName/sadl/sap/I_CFD_TSM_SO_CORE/0002/", "sadl/sap/I_CFD_TSM_SO_CORE", "0002");
		});

		QUnit.test("Parse OData v4 service document (with segment but no query parameters)", function(assert) {
			checkService(assert, "/sap/opu/odata4/grpNs/grpName/sadl/sap/i_cfd_tsm_so_core/0001;o=LOCAL_TGW", "sadl/sap/i_cfd_tsm_so_core", "0001");
			checkService(assert, "/sap/opu/odata4/grpNs/grpName/sadl/sap/i_cfd_tsm_so_core/0002;o=LOCAL_TGW/", "sadl/sap/i_cfd_tsm_so_core", "0002");
		});

		QUnit.test("Parse OData v4 service entity (with segment and query parameters)", function(assert) {
			checkService(assert, "/sap/opu/odata4/grpNs/grpName/sadl/sap/i_cfd_tsm_so_core/0001;o=LOCAL_TGW/I_CfdTsm_Bupa?$format=json", "sadl/sap/i_cfd_tsm_so_core", "0001");
			checkService(assert, "/sap/opu/odata4/grpNs/grpName/sadl/sap/i_cfd_tsm_so_core/0001;o=LOCAL_TGW/I_CfdTsm_Bupa/?$format=json", "sadl/sap/i_cfd_tsm_so_core", "0001");
		});

		QUnit.test("Parse OData v4 service entity navigation (with segment and query parameters)", function(assert) {
			checkService(assert, "/sap/opu/odata4/grpNs/grpName/sadl/sap/i_cfd_tsm_so_core/0001;o=LOCAL_TGW/I_CfdTsm_Bupa/toAss?$format=json", "sadl/sap/i_cfd_tsm_so_core", "0001");
			checkService(assert, "/sap/opu/odata4/grpNs/grpName/sadl/sap/i_cfd_tsm_so_core/0001;o=LOCAL_TGW/I_CfdTsm_Bupa/toAss/?$format=json", "sadl/sap/i_cfd_tsm_so_core", "0001");
		});

		QUnit.test("Check correct escaping in _buildBusinessContextRetrievalUri", function(assert) {
			var sBusinessContextRetrievalUri = Access._buildBusinessContextRetrievalUri({
				serviceName: "sadl/sap/i_cfd_tsm_so_core",
				serviceVersion: "0001",
				serviceType: "v4"
			}, {
				entityTypeName: "BusinessPartner",
				entitySetName: ""
			});
			var sExpectedBusinessContextRetrievalUri = "/sap/opu/odata/SAP/APS_CUSTOM_FIELD_MAINTENANCE_SRV/GetBusinessContextsByResourcePath?ResourcePath=%27sap%2fopu%2fodata4%2fsadl%2fsap%2fi_cfd_tsm_so_core%2f0001%27&EntitySetName=''&EntityTypeName='BusinessPartner'&$format=json";
			assert.equal(sBusinessContextRetrievalUri, sExpectedBusinessContextRetrievalUri);
		});

		QUnit.test("Call getBusinessContexts for V4 service against old backend", function(assert) {
			var sServiceUrl = "/sap/opu/odata4/grpNs/grpName/sadl/sap/i_cfd_tsm_so_core/0001/";
			var sEntityTypeName = "BusinessPartner";
			var oServer = sinon.fakeServer.create();
			oServer.autoRespond = true;
			var oExpectedResult = {
				BusinessContexts: [],
				ServiceName: "sadl/sap/i_cfd_tsm_so_core",
				ServiceVersion: "0001"
			};
			var oMockResponse = {
				error: {
					code: "005056A509B11EE1B9A8FEC11C21D78E",
					message: {
						lang: "en",
						value: "Resource not found for the segment 'GetBusinessContextsByResourcePath'."
					},
					innererror: {
						transactionid: "80E2B6AC2FB801F0E005A26F672AEE62",
						timestamp: "20171206180317.9903950",
						Error_Resolution: {
							SAP_Transaction: "For backend administrators: run transaction /IWFND/ERROR_LOG on SAP Gateway hub system and search for entries with the timestamp above for more details",
							SAP_Note: "See SAP Note 1797736 for error analysis (https://service.sap.com/sap/support/notes/1797736)"
						}
					}
				}
			};

			try {
				var oPromise = Access.getBusinessContexts(sServiceUrl, sEntityTypeName);

				oPromise.done(function(oBusinessContexts) {
					oServer.restore();
					assert.deepEqual(oBusinessContexts, oExpectedResult);
				});

				oPromise.fail(function() {
					oServer.restore();
					assert.ok(false, "Should not run into fail branch");
				});

				oServer.requests[0].respond(404, {
					"Content-Type": "application/json",
					"Content-Length": 13,
					"X-CSRF-Token": "0987654321"
				}, JSON.stringify(oMockResponse));
			} catch (e) {
				oServer.restore();
				assert.ok(false, e);
			}
		});
	});

	QUnit.module("Interface Functions", {
		afterEach: function() {
			sandbox.restore();
		}
	}, function() {
		QUnit.test("getTexts", function(assert) {
			var oResourceBundle = Core.getLibraryResourceBundle("sap.ui.fl");
			var oGetTextStub = sandbox.stub(oResourceBundle, "getText").callsFake(function(sTextKey) {return sTextKey;});

			var mExpectedTexts = {
				tooltip: "BTN_FREP_CCF",
				headerText: "BUSINESS_CONTEXT_TITLE"
			};
			var mTexts = Access.getTexts();

			assert.equal(oGetTextStub.callCount, 2, "two texts were retrieved");
			assert.deepEqual(mTexts, mExpectedTexts, "the correct texts were retrieved");
		});

		QUnit.test("isExtensibilityEnabled with empty string as component class name", function(assert) {
			sandbox.stub(Utils, "getComponentClassName").returns("");

			return Access.isExtensibilityEnabled().then(function(bExtensibilityEnabled) {
				assert.equal(bExtensibilityEnabled, false, "the return value is false");
			});
		});

		QUnit.test("isExtensibilityEnabled with undefined as component class name", function(assert) {
			sandbox.stub(Utils, "getComponentClassName").returns();

			return Access.isExtensibilityEnabled().then(function(bExtensibilityEnabled) {
				assert.equal(bExtensibilityEnabled, false, "the return value is false");
			});
		});

		QUnit.test("isExtensibilityEnabled with undefined as component class name", function(assert) {
			sandbox.stub(Utils, "getComponentClassName").returns("name");
			sandbox.stub(Settings, "getInstance").resolves({
				isModelS: function() {
					return true;
				}
			});

			return Access.isExtensibilityEnabled().then(function(bExtensibilityEnabled) {
				assert.equal(bExtensibilityEnabled, true, "the function returns the value if isModelS");
			});
		});

		QUnit.test("onTriggerCreateExtensionData", function(assert) {
			var oHrefForExternalStub = sandbox.stub().returns("foo");
			sandbox.stub(Utils, "ifUShellContainerThen").callsFake(function(fnCallback) {
				fnCallback([{
					hrefForExternal: oHrefForExternalStub
				}]);
			});
			var oOpenNewWindowStub = sandbox.stub(Access, "openNewWindow");
			var oExtensibilityInfo = {
				extensionData: [
					{
						businessContext: "bar"
					},
					{
						businessContext: "foobar"
					}
				],
				serviceName: "name",
				serviceVersion: 1,
				entityType: "odata"
			};

			var oExpectedParams = {
				target: {
					semanticObject: "CustomField",
					action: "develop"
				},
				params: {
					businessContexts: ["bar", "foobar"],
					serviceName: "name",
					serviceVersion: 1,
					entityType: "odata"
				}
			};

			Access.onTriggerCreateExtensionData(oExtensibilityInfo);
			assert.deepEqual(oExpectedParams, oHrefForExternalStub.lastCall.args[0], "the function was called with the correct parameters");
			assert.equal("foo", oOpenNewWindowStub.lastCall.args[0], "the function was called with the correct parameters");
		});
	});

	QUnit.module("Given a complex test view with oData Model...", {
		before: function () {
			return XMLView.create({
				id: "idMain1",
				viewName: "sap.ui.rta.test.additionalElements.ComplexTest"
			}).then(function(oView) {
				this.oView = oView;
				this.oView.placeAt("qunit-fixture");
				sap.ui.getCore().applyChanges();
				return this.oView.getController().isDataReady();
			}.bind(this));
		},
		afterEach: function() {
			sandbox.restore();
		},
		after: function () {
			this.oView.destroy();
		}
	}, function() {
		QUnit.test("getExtensionData without BusinessContexts", function(assert) {
			var oGetBusinessContextsStub = sandbox.stub(Access, "getBusinessContexts").resolves();
			return Access.getExtensionData(this.oView.byId("EntityType01.Prop1")).then(function(oExtensionData) {
				assert.equal(oExtensionData, false, "the function returns false");
				assert.equal("/destinations/E91/sap/opu/odata/SAP/AdditionalElementsTest", oGetBusinessContextsStub.lastCall.args[0]);
				assert.equal("EntityType01", oGetBusinessContextsStub.lastCall.args[1]);
			});
		});

		QUnit.test("getExtensionData with BusinessContexts", function(assert) {
			var oGetBusinessContextsStub = sandbox.stub(Access, "getBusinessContexts").resolves({
				BusinessContexts: [
					"foo"
				],
				ServiceName: "name",
				ServiceVersion: "2"
			});
			var oExpectedResult = {
				extensionData: [
					"foo"
				],
				entityType: "EntityType01",
				serviceName: "name",
				serviceVersion: "2"
			};
			return Access.getExtensionData(this.oView.byId("EntityType01.Prop1")).then(function(oExtensionData) {
				assert.deepEqual(oExtensionData, oExpectedResult, "the function returns the Extension data");
				assert.equal(oGetBusinessContextsStub.callCount, 1, "the Business Contexts were retrieved");
			});
		});

		QUnit.test("getExtensionData with empty BusinessContexts", function(assert) {
			var oGetBusinessContextsStub = sandbox.stub(Access, "getBusinessContexts").resolves({
				BusinessContexts: []
			});
			return Access.getExtensionData(this.oView.byId("EntityType01.Prop1")).then(function(oExtensionData) {
				assert.equal(oExtensionData, false, "the function returns false");
				assert.equal(oGetBusinessContextsStub.callCount, 1, "the Business Contexts were retrieved");
			});
		});

		QUnit.test("getExtensionData with getBusinessContexts throwing an error", function(assert) {
			var oLogStub = sandbox.stub(Log, "error");
			var oError = Error("myError");
			oError.errorMessages = [
				{
					text: "a"
				},
				{
					text: "b"
				}
			];
			var oGetBusinessContextsStub = sandbox.stub(Access, "getBusinessContexts").rejects(oError);
			return Access.getExtensionData(this.oView.byId("EntityType01.Prop1")).then(function(oExtensionData) {
				assert.ok(true, "the function resolves");
				assert.equal(oExtensionData, false, "the function returns false");
				assert.equal(oGetBusinessContextsStub.callCount, 1, "the Business Contexts were retrieved");
				assert.equal(oLogStub.callCount, 2, "two errors was logged");
			});
		});
	});

	QUnit.done(function () {
		jQuery("#qunit-fixture").hide();
	});
});