function hasPendingChangesForPath(sPath) {
			return oCache.hasPendingChangesForPath(sPath, false, bIgnoreTransient);
		}
******************************************************************************************
	QUnit.test("_Cache basics", function (assert) {
		var mQueryOptions = {},
			sResourcePath = "TEAMS('42')",
			oCache;

		this.mock(_Cache.prototype).expects("setQueryOptions")
			.withExactArgs(sinon.match.same(mQueryOptions)).twice()
			.callsFake(function () {
				assert.strictEqual(this.bSortExpandSelect, "bSortExpandSelect");
				assert.notOk(this.bSharedRequest);
			});
		this.mock(_Cache.prototype).expects("setResourcePath").twice()
			.withExactArgs(sResourcePath)
			.callsFake(function () {
				assert.notOk(this.bSharedRequest);
			});

		// code under test
		oCache = new _Cache(this.oRequestor, sResourcePath, mQueryOptions,
			"bSortExpandSelect", "original/resource/path", "bSharedRequest");

		assert.strictEqual(oCache.iActiveUsages, 1);
		assert.deepEqual(oCache.mChangeListeners, {});
		assert.strictEqual(oCache.sOriginalResourcePath, "original/resource/path");
		assert.strictEqual(oCache.iInactiveSince, Infinity);
		assert.deepEqual(oCache.mChangeRequests, {});
		assert.deepEqual(oCache.mEditUrl2PatchPromise, {});
		assert.deepEqual(oCache.mPostRequests, {});
		assert.strictEqual(oCache.oPendingRequestsPromise, null);
		assert.strictEqual(oCache.getPendingRequestsPromise(), null);
		assert.strictEqual(oCache.oRequestor, this.oRequestor);
		assert.strictEqual(oCache.bSortExpandSelect, "bSortExpandSelect");
		assert.strictEqual(oCache.bSentRequest, false);
		assert.strictEqual(oCache.bSharedRequest, "bSharedRequest");
		assert.strictEqual(oCache.sReportedMessagesPath, undefined);
		assert.ok(oCache.hasOwnProperty("sReportedMessagesPath"));

		assert.throws(function () {
			// code under test
			oCache.getValue();
		}, new Error("Unsupported operation"));

		// code under test
		oCache = new _Cache(this.oRequestor, sResourcePath, mQueryOptions, "bSortExpandSelect");

		assert.strictEqual(oCache.sOriginalResourcePath, sResourcePath);
	});

	//*********************************************************************************************
	QUnit.test("_Cache#setQueryOptions defaulting", function (assert) {
		const oCache = new _Cache(this.oRequestor);

		// code under test
		oCache.setQueryOptions();

		assert.deepEqual(oCache.mQueryOptions, {});

		oCache.bSentRequest = true;

		assert.throws(() => {
			// code under test
			oCache.setQueryOptions();
		}, new Error("Cannot set query options: Cache has already sent a request"));
	});

	//*********************************************************************************************
	QUnit.test("_Cache#setResourcePath", function (assert) {
		var oCache = new _Cache(this.oRequestor, "TEAMS('42')/name.space.Operation");

		oCache.mLateQueryOptions = {};
		oCache.mPropertyRequestByPath = {};
		oCache.oTypePromise = {};
		this.mock(oCache).expects("checkSharedRequest").withExactArgs();
		this.mock(_Helper).expects("getMetaPath").withExactArgs("/TEAMS('23')").returns("/TEAMS");

		// code under test
		oCache.setResourcePath("TEAMS('23')");

		assert.strictEqual(oCache.sResourcePath, "TEAMS('23')");
		assert.strictEqual(oCache.sMetaPath, "/TEAMS");
		assert.strictEqual(oCache.oTypePromise, undefined);
		assert.ok(oCache.hasOwnProperty("oTypePromise"));
		assert.strictEqual(oCache.mLateQueryOptions, null);
		assert.deepEqual(oCache.mPropertyRequestByPath, {});
	});

	//*********************************************************************************************
	QUnit.test("_Cache#setProperty", function () {
		var oCache = new _Cache(this.oRequestor, "TEAMS('42')"),
			oEntity = {},
			oEntityPromise = Promise.resolve(oEntity),
			oUpdateData = {},
			that = this;

		oCache.fetchValue = function () {};
		this.mock(oCache).expects("checkSharedRequest").withExactArgs();
		this.mock(oCache).expects("fetchValue")
			.withExactArgs(sinon.match.same(_GroupLock.$cached), "path/to/entity", null, null, true)
			.returns(SyncPromise.resolve(oEntityPromise));
		oEntityPromise.then(function () {
			that.mock(_Helper).expects("makeUpdateData")
				.withExactArgs(["Address", "City"], "Walldorf", "~bUpdating~")
				.returns(oUpdateData);
			that.mock(_Helper).expects("updateAll")
				.withExactArgs(sinon.match.same(oCache.mChangeListeners), "path/to/entity",
					sinon.match.same(oEntity), sinon.match.same(oUpdateData));
		});

		// code under test
		return oCache.setProperty("Address/City", "Walldorf", "path/to/entity", "~bUpdating~");
	});

	//*********************************************************************************************
	QUnit.test("_Cache#get-/setQueryOptions", function (assert) {
		var sMetaPath = "/TEAMS",
			mNewQueryOptions = {},
			mQueryOptions = {},
			oCache;

		this.oRequestorMock.expects("buildQueryString")
			.withExactArgs(sMetaPath, sinon.match.same(mQueryOptions), false, "bSortExpandSelect")
			.returns("?foo=bar");
		oCache = new _Cache(this.oRequestor, "TEAMS('42')", mQueryOptions, "bSortExpandSelect");
		assert.strictEqual(oCache.mQueryOptions, mQueryOptions);
		assert.strictEqual(oCache.sQueryString, "?foo=bar");

		// code under test
		assert.strictEqual(oCache.getQueryOptions(), mQueryOptions);

		this.mock(oCache).expects("checkSharedRequest").thrice().withExactArgs();
		this.oRequestorMock.expects("buildQueryString")
			.withExactArgs(sMetaPath, sinon.match.same(mNewQueryOptions), false,
				"bSortExpandSelect")
			.returns("?baz=boo");

		// code under test
		oCache.setQueryOptions(mNewQueryOptions);

		assert.strictEqual(oCache.mQueryOptions, mNewQueryOptions);
		assert.strictEqual(oCache.sQueryString, "?baz=boo");

		// code under test
		assert.strictEqual(oCache.getQueryOptions(), mNewQueryOptions);

		oCache.bSentRequest = true;

		// code under test
		assert.throws(function () {
			oCache.setQueryOptions(mQueryOptions);
		}, new Error("Cannot set query options: Cache has already sent a request"));

		this.oRequestorMock.expects("buildQueryString")
			.withExactArgs(sMetaPath, sinon.match.same(mQueryOptions), false,
				"bSortExpandSelect")
			.returns("?foo=bar");

		// code under test
		oCache.setQueryOptions(mQueryOptions, true);

		assert.strictEqual(oCache.mQueryOptions, mQueryOptions);
		assert.strictEqual(oCache.sQueryString, "?foo=bar");

		// code under test
		assert.strictEqual(oCache.getQueryOptions(), mQueryOptions);
	});

	//*********************************************************************************************
	QUnit.test("_Cache hierarchy", function (assert) {
		assert.ok(_Cache.create(this.oRequestor, "TEAMS") instanceof _Cache);
		assert.ok(_Cache.createSingle(this.oRequestor, "TEAMS('42')") instanceof _Cache);
		assert.ok(_Cache.createProperty(this.oRequestor, "TEAMS('42')/Team_Id") instanceof _Cache);
		assert.ok(_Cache.createProperty(this.oRequestor, "Singleton/Team_Id") instanceof _Cache);
	});

	//*********************************************************************************************
	QUnit.test("_Cache: single cache with optional meta path", function (assert) {
		var sMetaPath = "/com.sap.gateway.default.iwbep.tea_busi.v0001.TEAM",
			oSingleCache = _Cache.createSingle(this.oRequestor, "TEAMS('42')", {}, false, false,
				undefined, false, sMetaPath);

		assert.strictEqual(oSingleCache.sMetaPath, sMetaPath);
		assert.strictEqual(oSingleCache.oPromise, null);

		this.oRequestorMock.expects("fetchType").withExactArgs(sinon.match.object, sMetaPath)
			.returns(SyncPromise.resolve());

		// code under test
		oSingleCache.fetchTypes();
	});

	//*********************************************************************************************
[ // iStatus === -1 => The request is canceled
	{bCreated : false, oEntity : undefined, iStatus : 200, sPath : ""},
	{bCreated : true, oEntity : undefined, iStatus : 200, sPath : ""},
	{bCreated : false, oEntity : undefined, iStatus : 404, sPath : ""},
	{bCreated : false, oEntity : undefined, iStatus : 500, sPath : ""},
	{bCreated : true, oEntity : undefined, iStatus : 500, sPath : "EMPLOYEE_2_EQUIPMENTS"},
	{bCreated : true, oEntity : undefined, iStatus : 500, sPath : "", bMessagesToRestore : true},
	{bCreated : false, oEntity : undefined, iStatus : 500, sPath : "", bInactive : true},
	{bCreated : true, oEntity : undefined, iStatus : -1, sPath : "EMPLOYEE_2_EQUIPMENTS"},
	{bCreated : true, oEntity : undefined, iStatus : -1, sPath : "", bMessagesToRestore : true},
	{bCreated : false, oEntity : undefined, iStatus : -1, sPath : "", bInactive : true},
	{bCreated : false, oEntity : {"@odata.etag" : "AnotherETag"}, iStatus : 200, sPath : ""}
].forEach(function (oFixture) {
	QUnit.test("_Cache#_delete: from collection, status: " + oFixture.iStatus
			+ ", created: " + oFixture.bCreated
			+ (oFixture.oEntity ? " (ETagEntity)" : ""), function (assert) {
		var that = this,
			bAddDeleted = false,
			mQueryOptions = {foo : "bar"},
			oCache = new _Cache(this.oRequestor, "EMPLOYEES('42')", mQueryOptions, false,
				"original/resource/path"),
			sEtag = 'W/"19770724000000.0000000"',
			aCacheData = [{}, {
				"@$ui5._" : {
					predicate : "('1')",
					transientPredicate : oFixture.bCreated ? "($uid=id-1-23)" : undefined
				},
				"@odata.etag" : sEtag
			}, {}],
			fnCallback = this.spy(),
			oDeleted = {index : "~insert~"},
			oError = new Error(""),
			oGroupLock = {getGroupId : function () {}},
			oHelperMock = this.mock(_Helper),
			oMessage1 = {code : "CODE1"},
			oMessage2 = {code : "CODE2", persistent : true},
			oMessageExpectation,
			aMessages = oFixture.bMessagesToRestore ? [oMessage1, oMessage2] : [],
			sPath = oFixture.sPath,
			oPromise,
			oRequestExpectation,
			oRequestPromise,
			oRestoreExpectation,
			bSuccess = oFixture.iStatus === 200 || oFixture.iStatus === 404;

		function checkCleanedUp() {
			assert.notOk("@$ui5.context.isDeleted" in aCacheData[1]);
			assert.deepEqual(aCacheData.$deleted, ["a", "b", "c"]);
			if (oFixture.bInactive) {
				sinon.assert.calledOnceWithExactly(fnCallback, 1, -1);
			} else {
				sinon.assert.calledTwice(fnCallback);
				sinon.assert.calledWithExactly(fnCallback.secondCall, "~insert~", 1);
			}
		}

		oRequestPromise = Promise.resolve().then(function () {
			var iOnFailure = bSuccess ? 0 : 1;

			that.mock(_Helper).expects("removeByPath")
				.withExactArgs(sinon.match.same(oCache.mChangeRequests), sPath + "('1')",
					sinon.match.same(oRequestPromise));
			if (oFixture.bInactive) {
				oCache.iActiveUsages = 0;
			}
			oMessageExpectation = that.oModelInterfaceMock.expects("updateMessages")
				.exactly(oFixture.bMessagesToRestore ? 1 : 0)
				.withExactArgs(undefined, oFixture.iStatus < 0
					? [oMessage1, oMessage2] : [oMessage1]);
			oRestoreExpectation = that.mock(oCache).expects("restoreElement").exactly(iOnFailure)
				.withExactArgs("~insert~", sinon.match.same(aCacheData[1]), 2,
					sinon.match.same(aCacheData), sPath)
				.callsFake(() => {
					assert.deepEqual(aCacheData.$deleted.length, 4);
				});
			if (oFixture.iStatus !== 200) {
				if (oFixture.iStatus < 0) { // simulate the cancel
					oError.canceled = true;

					// code under test - call fnCancel
					oRequestExpectation.args[0][6]();

					assert.strictEqual(oMessageExpectation.called, !!oFixture.bMessagesToRestore);
					assert.ok(oRestoreExpectation.called);
					checkCleanedUp();
				}
				throw oError;
			}
		});

		aCacheData.$byPredicate = {"('1')" : aCacheData[1]};
		this.mock(oCache).expects("checkSharedRequest").withExactArgs();
		this.mock(oCache).expects("addPendingRequest").never();
		this.mock(oCache).expects("removePendingRequest").never();
		oCache.fetchValue = function () {};
		this.mock(oCache).expects("fetchValue")
			.withExactArgs(sinon.match.same(_GroupLock.$cached), sPath)
			.returns(SyncPromise.resolve(aCacheData));
		oError.status = oFixture.iStatus;
		oHelperMock.expects("getPrivateAnnotation")
			.withExactArgs(aCacheData[1], "predicate").callThrough();
		oHelperMock.expects("getPrivateAnnotation")
			.withExactArgs(aCacheData[1], "transient").callThrough();
		oHelperMock.expects("getPrivateAnnotation")
			.withExactArgs(aCacheData[1], "transientPredicate").callThrough();
		this.oModelInterfaceMock.expects("getMessagesByPath")
			.withExactArgs("/" + oCache.sResourcePath + (sPath ? "/" + sPath : "") + "('1')", true)
			.returns(aMessages);
		this.oModelInterfaceMock.expects("updateMessages")
			.withExactArgs(sinon.match.same(aMessages));
		this.mock(oCache).expects("addDeleted")
			.withExactArgs(sinon.match.same(aCacheData), 1, "('1')", sinon.match.same(oGroupLock),
				oFixture.bCreated)
			.callsFake(function () {
				bAddDeleted = true;
				aCacheData.$deleted = ["a", "b", oDeleted, "c"];
				return oDeleted;
			});
		this.mock(oCache).expects("removeElement")
			.withExactArgs(1, "('1')", sinon.match.same(aCacheData), sPath)
			.callsFake(function () {
				assert.ok(bAddDeleted, "removeElement called after addDeleted");
			});
		this.mock(oGroupLock).expects("getGroupId").withExactArgs().returns("~group~");
		this.oRequestorMock.expects("relocateAll")
			.withExactArgs("$parked.~group~", "~group~", sinon.match.same(aCacheData[1]));
		this.oRequestorMock.expects("buildQueryString")
			.withExactArgs("/EMPLOYEES", sinon.match.same(mQueryOptions), true)
			.returns("?foo=bar");
		oRequestExpectation = this.oRequestorMock.expects("request")
			.withExactArgs("DELETE", "Equipments('1')?foo=bar", sinon.match.same(oGroupLock),
				{"If-Match" : sinon.match.same(oFixture.oEntity || aCacheData[1])}, undefined,
				undefined, sinon.match.func, undefined,
				"original/resource/path" + (sPath && "/" + sPath) + "('1')")
			.returns(oRequestPromise);
		this.mock(_Helper).expects("addByPath")
			.withExactArgs(sinon.match.same(oCache.mChangeRequests), sPath + "('1')",
				sinon.match.same(oRequestPromise));

		// code under test
		oPromise = oCache._delete(oGroupLock, "Equipments('1')", (sPath && sPath + "/") + "1",
			oFixture.oEntity, fnCallback);

		assert.strictEqual(aCacheData[1]["@$ui5.context.isDeleted"], true);
		sinon.assert.calledOnceWithExactly(fnCallback, 1, -1);

		return oPromise.then(function () {
			assert.ok(bSuccess);
			assert.deepEqual(aCacheData.$deleted, ["a", "b", "c"]);
			assert.notOk("('1')" in aCacheData.$byPredicate);
		}, function (oError0) {
			assert.notOk(bSuccess);
			assert.strictEqual(oError0, oError);
			checkCleanedUp();
		});
	});
});

	//TODO adjust paths in mChangeRequests?
	//TODO invoke update in case of isConcurrentModification?!
	//TODO do it anyway? what and when to return, result of remove vs. re-read?

	//*********************************************************************************************
[false, true].forEach(function (bLock) {
	QUnit.test("_Cache#_delete: nested entity" + (bLock ? "" : ", no lock"), function () {
		var oCache = new _Cache(this.oRequestor, "Equipments(Category='foo',ID='0815')",
				{$expand : {EQUIPMENT_2_EMPLOYEE : {EMPLOYEE_2_TEAM : true}}}),
			sEtag = 'W/"19770724000000.0000000"',
			oCacheData = {
				EMPLOYEE_2_TEAM : {
					"@$ui5._" : {
						predicate : "('23')"
					},
					"@odata.etag" : sEtag
				}
			},
			oGroupLock = bLock ? {getGroupId : function () {}} : null,
			sPath = "Equipments(Category='foo',ID='0815')/EQUIPMENT_2_EMPLOYEE/EMPLOYEE_2_TEAM",
			oUpdateData = {};

		oCache.fetchValue = function () {};
		this.mock(oCache).expects("fetchValue")
			.withExactArgs(sinon.match.same(_GroupLock.$cached), "EQUIPMENT_2_EMPLOYEE")
			.returns(SyncPromise.resolve(oCacheData));
		if (bLock) {
			this.mock(oGroupLock).expects("getGroupId").withExactArgs().returns("~group~");
			this.oRequestorMock.expects("relocateAll")
				.withExactArgs("$parked.~group~", "~group~",
					sinon.match.same(oCacheData["EMPLOYEE_2_TEAM"]));
			this.oRequestorMock.expects("request")
				.withExactArgs("DELETE", "TEAMS('23')", oGroupLock, {
						"If-Match" : sinon.match.same(oCacheData["EMPLOYEE_2_TEAM"])
					}, undefined, undefined, sinon.match.func, undefined, sPath)
				.returns(Promise.resolve());
		} else {
			this.oRequestorMock.expects("request").never();
			this.oModelInterfaceMock.expects("getMessagesByPath")
				.withExactArgs("/" + sPath, true).returns("~aMessages~");
			this.oModelInterfaceMock.expects("updateMessages")
				.withExactArgs("~aMessages~");
		}
		this.mock(_Helper).expects("makeUpdateData").withExactArgs(["EMPLOYEE_2_TEAM"], null)
			.returns(oUpdateData);
		this.mock(_Helper).expects("updateExisting")
			.withExactArgs(sinon.match.same(oCache.mChangeListeners), "EQUIPMENT_2_EMPLOYEE",
				sinon.match.same(oCacheData), sinon.match.same(oUpdateData));

		// code under test
		return oCache._delete(oGroupLock, "TEAMS('23')", "EQUIPMENT_2_EMPLOYEE/EMPLOYEE_2_TEAM",
			null, /*fnCallback