import { expect } from "chai";
import { ElementLRUCache, InstanceKeyLRUCache } from "../internal/ElementLRUCache";
import { Id64 } from "@itwin/core-bentley";
describe('ElementLruCache', () => {
    const testElem1 = {
        loadOptions: {},
        elProps: {
            id: "testId",
            model: "testModel",
            code: {
                spec: "testSpec",
                scope: "testScope"
            },
            classFullName: "testName"
        },
    };
    const testElem2 = {
        loadOptions: {},
        elProps: {
            id: "testId2",
            model: "testModel2",
            code: {
                spec: "testSpec2",
                scope: "testScope2",
                value: "testValue2"
            },
            classFullName: "testName2"
        },
    };
    const testElem3 = {
        loadOptions: {},
        elProps: {
            id: "testId3",
            model: "testModel3",
            code: {
                spec: "testSpec3",
                scope: "testScope3"
            },
            classFullName: "testName3"
        },
    };
    const testElemFedGuid = {
        loadOptions: {},
        elProps: {
            id: "testIdFed",
            model: "testModel",
            code: {
                spec: "testSpec",
                scope: "testScope"
            },
            classFullName: "testName",
            federationGuid: "testFedGuid"
        },
    };
    const testElemCode = {
        loadOptions: {},
        elProps: {
            id: "testId5",
            model: "testModel5",
            code: {
                spec: "testSpec2:testValue2",
                scope: "testScope2"
            },
            classFullName: "testName5",
            federationGuid: "testFedGuid5"
        },
    };
    const testElemNoId = {
        loadOptions: {},
        elProps: {
            id: undefined,
            model: "testModel5",
            code: {
                spec: "testSpec2",
                scope: "testScope2"
            },
            classFullName: "testName5",
            federationGuid: "testFedGuid5"
        },
    };
    it('should store and retrieve a valid element', () => {
        const cache = new ElementLRUCache(3);
        cache.set(testElem1);
        const retrievedElem = cache.get({ id: testElem1.elProps.id });
        expect(retrievedElem).to.not.be.undefined;
        expect(retrievedElem).to.equal(testElem1);
    });
    it('should overwrite existing valid element', () => {
        const cache = new ElementLRUCache(3);
        const testElemModel = testElem1;
        cache.set(testElemModel);
        const retrievedElem = cache.get({ id: testElemModel.elProps.id });
        expect(retrievedElem).to.not.be.undefined;
        expect(retrievedElem).to.equal(testElemModel);
        testElemModel.elProps.model = "newModel";
        cache.set(testElemModel);
        const retrievedElem2 = cache.get({ id: testElemModel.elProps.id });
        expect(retrievedElem2).to.not.be.undefined;
        expect(retrievedElem2).to.equal(testElemModel);
    });
    it('should store and retrieve a valid element by fedGuid', () => {
        const cache = new ElementLRUCache(3);
        cache.set(testElemFedGuid);
        const retrievedElem = cache.get({ federationGuid: testElemFedGuid.elProps.federationGuid });
        expect(retrievedElem).to.not.be.undefined;
        expect(retrievedElem).to.equal(testElemFedGuid);
    });
    it('should store and retrieve a valid element by code', () => {
        const cache = new ElementLRUCache(3);
        cache.set(testElem1);
        const retrievedElem = cache.get({ code: testElem1.elProps.code });
        expect(retrievedElem).to.not.be.undefined;
        expect(retrievedElem).to.equal(testElem1);
        cache.set(testElem2);
        const retrievedElem2 = cache.get({ code: testElem2.elProps.code });
        expect(retrievedElem2).to.not.be.undefined;
        expect(retrievedElem2).to.equal(testElem2);
    });
    it('should fail to store an element without id', () => {
        const cache = new ElementLRUCache(3);
        expect(() => cache.set(testElemNoId)).to.throw(Error, "Element must have an id");
    });
    it('should delete least used element', () => {
        const cache = new ElementLRUCache(3);
        cache.set(testElem1);
        cache.set(testElem2);
        cache.set(testElem3);
        cache.set(testElemFedGuid);
        expect(cache.size).to.equal(3);
        const retrievedElem = cache.get({ id: testElem2.elProps.id });
        expect(retrievedElem).to.not.be.undefined;
        expect(retrievedElem).to.equal(testElem2);
        const retrievedElem2 = cache.get({ id: testElem1.elProps.id });
        expect(retrievedElem2).to.be.undefined;
        const retrievedElem3 = cache.get({ id: testElem3.elProps.id });
        expect(retrievedElem3).to.not.be.undefined;
        expect(retrievedElem3).to.equal(testElem3);
    });
    it('should delete with id', () => {
        const cache = new ElementLRUCache(3);
        cache.set(testElem1);
        cache.set(testElem2);
        cache.set(testElem3);
        expect(cache.size).to.equal(3);
        cache.delete({ id: testElem2.elProps.id });
        expect(cache.size).to.equal(2);
        const retrievedElem = cache.get({ id: testElem2.elProps.id });
        expect(retrievedElem).to.be.undefined;
        const retrievedElem2 = cache.get({ id: testElem1.elProps.id });
        expect(retrievedElem2).to.not.be.undefined;
        expect(retrievedElem2).to.equal(testElem1);
    });
    it('should delete with federation guid', () => {
        const cache = new ElementLRUCache(3);
        cache.set(testElem1);
        cache.set(testElem2);
        cache.set(testElemFedGuid);
        expect(cache.size).to.equal(3);
        cache.delete({ federationGuid: testElemFedGuid.elProps.federationGuid });
        expect(cache.size).to.equal(2);
        const retrievedElem = cache.get({ id: testElemFedGuid.elProps.id });
        expect(retrievedElem).to.be.undefined;
        const retrievedElem2 = cache.get({ id: testElem1.elProps.id });
        expect(retrievedElem2).to.not.be.undefined;
        expect(retrievedElem2).to.equal(testElem1);
    });
    it('should delete with code', () => {
        const cache = new ElementLRUCache(3);
        cache.set(testElem1);
        cache.set(testElem2);
        cache.set(testElem3);
        expect(cache.size).to.equal(3);
        cache.delete({ code: testElem2.elProps.code });
        expect(cache.size).to.equal(2);
        const retrievedElem = cache.get({ id: testElem2.elProps.id });
        expect(retrievedElem).to.be.undefined;
        const retrievedElem2 = cache.get({ id: testElem1.elProps.id });
        expect(retrievedElem2).to.not.be.undefined;
        expect(retrievedElem2).to.equal(testElem1);
    });
    it('should delete with model id', () => {
        const cache = new ElementLRUCache(3);
        cache.set(testElem1);
        cache.set(testElem2);
        cache.set(testElem3);
        expect(cache.size).to.equal(3);
        cache.deleteWithModel(testElem2.elProps.model);
        expect(cache.size).to.equal(2);
        const retrievedElem = cache.get({ id: testElem2.elProps.id });
        expect(retrievedElem).to.be.undefined;
        const retrievedElem2 = cache.get({ id: testElem1.elProps.id });
        expect(retrievedElem2).to.not.be.undefined;
        expect(retrievedElem2).to.equal(testElem1);
    });
    it('should clear the cache', () => {
        const cache = new ElementLRUCache(3);
        cache.set(testElem1);
        cache.set(testElem2);
        cache.set(testElem3);
        expect(cache.size).to.equal(3);
        cache.clear();
        expect(cache.size).to.equal(0);
        const retrievedElem = cache.get({ id: testElem1.elProps.id });
        expect(retrievedElem).to.be.undefined;
    });
    it('should not return wrong element when passed a manipulated code value', () => {
        const cache = new ElementLRUCache(3);
        cache.set(testElem2);
        expect(cache.size).to.equal(1);
        const retrievedElem = cache.get({ code: testElemCode.elProps.code });
        expect(retrievedElem).to.be.undefined;
    });
});
describe('InstanceKeyLRUCache', () => {
    const testArgs1 = {
        partialKey: {
            id: Id64.fromJSON("0x123"),
            baseClassName: "baseName",
        },
    };
    const testArgs2 = {
        federationGuid: "testFedGuid",
    };
    const testArgs3 = {
        code: {
            spec: "testSpec",
            scope: "testScope",
            value: "testValue"
        }
    };
    const testArgs4 = {
        code: {
            spec: "testSpec",
            scope: "testScope"
        }
    };
    const testResults = [
        {
            id: Id64.fromJSON("0x123"),
            classFullName: "testName1",
        },
        {
            id: Id64.fromJSON("0x122"),
            classFullName: "testName2",
        },
        {
            id: Id64.fromJSON("0x133"),
            classFullName: "testName3",
        },
        {
            id: Id64.fromJSON("0x144"),
            classFullName: "testName4",
        }
    ];
    it('should store and retrieve a valid InstanceKey', () => {
        const cache = new InstanceKeyLRUCache(3);
        cache.set(testArgs1, testResults[0]);
        const retrievedResult = cache.get({ partialKey: testArgs1.partialKey });
        expect(retrievedResult).to.not.be.undefined;
        expect(retrievedResult).to.equal(testResults[0]);
    });
    it('should overwrite existing valid InstanceKey', () => {
        const cache = new InstanceKeyLRUCache(3);
        const testResult = testResults[0];
        cache.set(testArgs1, testResult);
        const retrievedResult = cache.get({ partialKey: testArgs1.partialKey });
        expect(retrievedResult).to.not.be.undefined;
        expect(retrievedResult).to.equal(testResult);
        const newTestResult = { id: testResult.id, classFullName: "newModel" };
        cache.set(testArgs1, newTestResult);
        const retrievedResult2 = cache.get({ partialKey: testArgs1.partialKey });
        expect(retrievedResult2).to.not.be.undefined;
        expect(retrievedResult2).to.equal(newTestResult);
    });
    it('should store and retrieve a valid InstanceKey by fedGuid', () => {
        const cache = new InstanceKeyLRUCache(3);
        cache.set(testArgs2, testResults[0]);
        const retrievedResult = cache.get({ federationGuid: testArgs2.federationGuid });
        expect(retrievedResult).to.not.be.undefined;
        expect(retrievedResult).to.equal(testResults[0]);
    });
    it('should store and retrieve a valid InstanceKey by code', () => {
        const cache = new InstanceKeyLRUCache(3);
        cache.set(testArgs3, testResults[0]);
        const retrievedResult = cache.get({ code: testArgs3.code });
        expect(retrievedResult).to.not.be.undefined;
        expect(retrievedResult).to.equal(testResults[0]);
        cache.set(testArgs4, testResults[1]);
        const retrievedResult2 = cache.get({ code: testArgs4.code });
        expect(retrievedResult2).to.not.be.undefined;
        expect(retrievedResult2).to.equal(testResults[1]);
    });
    it('should find the same InstanceKey with either code, fedGuid, or Id', () => {
        const cache = new InstanceKeyLRUCache(3);
        cache.set({ ...testArgs1, ...testArgs2, ...testArgs3 }, testResults[0]);
        const retrievedResultById = cache.get({ partialKey: testArgs1.partialKey });
        expect(retrievedResultById).to.not.be.undefined;
        expect(retrievedResultById).to.equal(testResults[0]);
        const retrievedResultByFedGuid = cache.get({ federationGuid: testArgs2.federationGuid });
        expect(retrievedResultByFedGuid).to.not.be.undefined;
        expect(retrievedResultByFedGuid).to.equal(testResults[0]);
        const retrievedResultByCode = cache.get({ code: testArgs3.code });
        expect(retrievedResultByCode).to.not.be.undefined;
        expect(retrievedResultByCode).to.equal(testResults[0]);
    });
    it('should update an existing InstanceKey with either code, fedGuid, or Id', () => {
        const cache = new InstanceKeyLRUCache(3);
        cache.set(testArgs1, testResults[0]);
        cache.set(testArgs2, testResults[0]);
        cache.set(testArgs3, testResults[0]);
        const retrievedResultById = cache.get({ partialKey: testArgs1.partialKey });
        expect(retrievedResultById).to.not.be.undefined;
        expect(retrievedResultById).to.equal(testResults[0]);
        const retrievedResultByFedGuid = cache.get({ federationGuid: testArgs2.federationGuid });
        expect(retrievedResultByFedGuid).to.not.be.undefined;
        expect(retrievedResultByFedGuid).to.equal(testResults[0]);
        const retrievedResultByCode = cache.get({ code: testArgs3.code });
        expect(retrievedResultByCode).to.not.be.undefined;
        expect(retrievedResultByCode).to.equal(testResults[0]);
    });
    it('should not store a bad id as result in the cache', () => {
        const cache = new InstanceKeyLRUCache(3);
        const badResult = { id: Id64.invalid, classFullName: "badName" };
        expect(() => cache.set(testArgs1, badResult)).to.throw(Error, "Invalid InstanceKey result");
        expect(cache.size).to.equal(0);
    });
    it('should delete least used element', () => {
        const cache = new InstanceKeyLRUCache(3);
        cache.set(testArgs1, testResults[0]);
        cache.set(testArgs2, testResults[1]);
        cache.set(testArgs3, testResults[2]);
        cache.set(testArgs4, testResults[3]);
        expect(cache.size).to.equal(3);
        const retrievedResult = cache.get(testArgs2);
        expect(retrievedResult).to.not.be.undefined;
        expect(retrievedResult).to.equal(testResults[1]);
        const retrievedResult2 = cache.get(testArgs1);
        expect(retrievedResult2).to.be.undefined;
        const retrievedResult3 = cache.get(testArgs3);
        expect(retrievedResult3).to.not.be.undefined;
        expect(retrievedResult3).to.equal(testResults[2]);
    });
    it('should delete with partialKey', () => {
        const cache = new InstanceKeyLRUCache(3);
        cache.set(testArgs1, testResults[0]);
        cache.set(testArgs2, testResults[1]);
        cache.set(testArgs3, testResults[2]);
        expect(cache.size).to.equal(3);
        cache.delete({ partialKey: testArgs1.partialKey });
        expect(cache.size).to.equal(2);
        const retrievedResult = cache.get({ partialKey: testArgs1.partialKey });
        expect(retrievedResult).to.be.undefined;
        const retrievedResult2 = cache.get(testArgs2);
        expect(retrievedResult2).to.not.be.undefined;
        expect(retrievedResult2).to.equal(testResults[1]);
    });
    it('should delete with id', () => {
        const cache = new InstanceKeyLRUCache(3);
        cache.set(testArgs1, testResults[0]);
        cache.set(testArgs2, testResults[1]);
        cache.set(testArgs3, testResults[2]);
        expect(cache.size).to.equal(3);
        cache.deleteById(testArgs1.partialKey.id);
        expect(cache.size).to.equal(2);
        const retrievedResult = cache.get({ partialKey: testArgs1.partialKey });
        expect(retrievedResult).to.be.undefined;
        const retrievedResult2 = cache.get(testArgs2);
        expect(retrievedResult2).to.not.be.undefined;
        expect(retrievedResult2).to.equal(testResults[1]);
    });
    it('should throw when deleting with a bad id', () => {
        const cache = new InstanceKeyLRUCache(3);
        cache.set(testArgs1, testResults[0]);
        cache.set(testArgs2, testResults[1]);
        cache.set(testArgs3, testResults[2]);
        expect(cache.size).to.equal(3);
        expect(() => cache.deleteById("badid")).to.throw(Error, "Invalid id provided");
        expect(cache.size).to.equal(3);
    });
    it('should delete with federation guid', () => {
        const cache = new InstanceKeyLRUCache(3);
        cache.set(testArgs1, testResults[0]);
        cache.set(testArgs2, testResults[1]);
        cache.set(testArgs3, testResults[2]);
        expect(cache.size).to.equal(3);
        cache.delete({ federationGuid: testArgs2.federationGuid });
        expect(cache.size).to.equal(2);
        const retrievedResult = cache.get({ federationGuid: testArgs2.federationGuid });
        expect(retrievedResult).to.be.undefined;
        const retrievedResult2 = cache.get(testArgs3);
        expect(retrievedResult2).to.not.be.undefined;
        expect(retrievedResult2).to.equal(testResults[2]);
    });
    it('should delete with code', () => {
        const cache = new InstanceKeyLRUCache(3);
        cache.set(testArgs1, testResults[0]);
        cache.set(testArgs2, testResults[1]);
        cache.set(testArgs3, testResults[2]);
        expect(cache.size).to.equal(3);
        cache.delete({ code: testArgs3.code });
        expect(cache.size).to.equal(2);
        const retrievedResult = cache.get({ code: testArgs3.code });
        expect(retrievedResult).to.be.undefined;
        const retrievedResult2 = cache.get(testArgs1);
        expect(retrievedResult2).to.not.be.undefined;
        expect(retrievedResult2).to.equal(testResults[0]);
    });
    it('should delete with any applicable arg', () => {
        const cache = new InstanceKeyLRUCache(3);
        cache.set(testArgs1, testResults[0]);
        cache.set(testArgs2, testResults[0]);
        cache.set(testArgs3, testResults[0]);
        expect(cache.size).to.equal(1);
        cache.delete(testArgs3);
        expect(cache.size).to.equal(0);
        const retrievedResult = cache.get(testArgs1);
        expect(retrievedResult).to.be.undefined;
        cache.set(testArgs1, testResults[0]);
        cache.set(testArgs2, testResults[1]);
        cache.set(testArgs3, testResults[2]);
        expect(cache.size).to.equal(3);
        cache.set(testArgs4, testResults[0]);
        expect(cache.size).to.equal(3);
        cache.delete(testArgs4);
        expect(cache.size).to.equal(2);
        const retrievedResult2 = cache.get(testArgs1);
        expect(retrievedResult2).to.be.undefined;
    });
    it('should throw when trying to delete with all undefined keys', () => {
        const cache = new InstanceKeyLRUCache(3);
        cache.set(testArgs1, testResults[0]);
        cache.set(testArgs2, testResults[1]);
        cache.set(testArgs3, testResults[2]);
        expect(cache.size).to.equal(3);
        expect(() => cache.delete({
            partialKey: undefined,
            federationGuid: undefined,
            code: undefined
        })).to.throw(Error, "ResolveInstanceKeyArgs must have a partialKey, code, or federationGuid");
        expect(cache.size).to.equal(3);
    });
    it('should clear the cache', () => {
        const cache = new InstanceKeyLRUCache(3);
        cache.set(testArgs1, testResults[0]);
        cache.set(testArgs2, testResults[1]);
        cache.set(testArgs3, testResults[2]);
        expect(cache.size).to.equal(3);
        cache.clear();
        expect(cache.size).to.equal(0);
        const retrievedElem = cache.get(testArgs1);
        expect(retrievedElem).to.be.undefined;
    });
});
//# sourceMappingURL=ElementLRUCache.test.js.map