UNPKG

jinaga

Version:

Data management for web and mobile applications.

316 lines (304 loc) 13.7 kB
import { hydrateFromTree, dehydrateFact } from '../../src/fact/hydrate'; import { FactRecord, FactReference } from '../../src/storage'; import { TopologicalSorter } from '../../src/fact/sorter'; describe('Topological sorter', () => { it('should accept empty array', () => { const sorter = givenTopologicalSorter(); const sorted = whenSort(sorter, []); expect(Object.keys(sorted)).toHaveLength(0); }); it('should accept single fact', () => { const sorter = givenTopologicalSorter(); const sorted = whenSort(sorter, dehydrateFact({ type: 'Singleton' }) ); expect(sorted.length).toEqual(1); expect(sorted[0].type).toEqual('Singleton'); }); it ('should accept facts of same hash', () => { const sorter = givenTopologicalSorter(); const sorted = whenSort(sorter, dehydrateFact({ type: 'First' }).concat(dehydrateFact({ type: 'Second' })) ); expect(sorted.length).toEqual(2); expect(sorted[0].type).toEqual('First'); expect(sorted[1].type).toEqual('Second'); }); it('should attribute predecessor of same hash', () => { const sorter = givenTopologicalSorter(); const sorted = whenSort(sorter, dehydrateFact({ type: 'First' }).concat(dehydrateFact({ type: 'Child', parent: { type: 'Second' } })) ); expect(sorted.length).toEqual(3); expect(sorted[0].type).toEqual('First'); expect(sorted[1].type).toEqual('Second'); expect(sorted[2].type).toEqual('Child'); const predecessor = <FactReference>sorted[2].predecessors.parent; expect(predecessor.type).toEqual('Second'); }); it('should wait for predecessor of correct type', () => { const facts = dehydrateFact({ type: 'First' }).concat(dehydrateFact({ type: 'Child', parent: { type: 'Second' } })); const unsorted = [ facts[0], facts[2], facts[1] ]; const sorter = givenTopologicalSorter(); const sorted = whenSort(sorter, unsorted); expect(sorted.length).toEqual(3); expect(sorted[0].type).toEqual('First'); expect(sorted[1].type).toEqual('Second'); expect(sorted[2].type).toEqual('Child'); const predecessor = <FactReference>sorted[2].predecessors.parent; expect(predecessor.type).toEqual('Second'); }); it('should handle successor before predecessor', () => { const facts = dehydrateFact({ type: 'Child', parent: { type: 'Parent' } }); const unsorted = [ facts[1], facts[0] ]; const sorter = givenTopologicalSorter(); const sorted = whenSort(sorter, unsorted); expect(sorted.length).toEqual(2); expect(sorted[0].type).toEqual('Parent'); expect(sorted[0].hash).toEqual(facts[0].hash); expect(sorted[1].type).toEqual('Child'); expect(sorted[1].hash).toEqual(facts[1].hash); expect(sorted[0].hash).not.toEqual(sorted[1].hash); }); it('should wait for all predecessors', () => { const facts = dehydrateFact({ type: 'Child', first: { type: 'First' }, second: { type: 'Second' } }); const unsorted = [ facts[2], facts[1], facts[0] ]; const sorter = givenTopologicalSorter(); const sorted = whenSort(sorter, unsorted); expect(sorted.length).toEqual(3); expect(sorted[2].type).toEqual('Child'); }) it('should handle a real world example', () => { const facts: FactRecord[] = [ { "type": "ImprovingU.Abstract", "hash": "gkCYyWW5BFEO+heluEKGpuYSFi7CdyXlF3AzH7DXeiR/GSGM5jIQpkOSazF1rcZXssdwqvrxaVmp1Qr58pu00g==", "predecessors": { "idea": { "type": "ImprovingU.Idea", "hash": "xWNSMeJNeHxDsrRqT0ChoAwptn1uEn7D3pxUQcmxELydfufS8IylnoSXTMvoctaqIFJeoAF1zbDbi+mqtIuCbQ==" }, "prior": [{ "type": "ImprovingU.Abstract", "hash": "ofFlc/jiNooYDrP1aKXsv15Ud0pBji+7k0KuZ4VneuKWCz02AHOi8yI+GZbIZZP9LShXf0mPxJgc2OKrA1zYPg==" } ], "from": { "type": "Jinaga.User", "hash": "0WWlFbZH+gMoP3QGXO7/mf6hIQC/2iN7wd0peEQKFVvnJMp3gTVvi4eXfVd3DSa81MSzAVp7zVxXIiRnJTF0Kw==" } }, "fields": { "value": "one\ntwo\nthree" } }, { "type": "ImprovingU.Abstract", "hash": "lXGr2y/MGnT283U5mF7oI+z/kZZ13kS2olxQD26vCCILIR5DmHG3orJcgNRkixmfuIUd7oGX9vdCwOZ+HGsgJg==", "predecessors": { "idea": { "type": "ImprovingU.Idea", "hash": "xWNSMeJNeHxDsrRqT0ChoAwptn1uEn7D3pxUQcmxELydfufS8IylnoSXTMvoctaqIFJeoAF1zbDbi+mqtIuCbQ==" }, "prior": [{ "type": "ImprovingU.Abstract", "hash": "Q473l768juQLIg1i3Lx/+6gtHKSRbyfY7GK5CzudvSKtMyhT6JxxUeYC6V5c3+uVWMAavnOsxXQPGX2ev/TVnw==" } ], "from": { "type": "Jinaga.User", "hash": "0WWlFbZH+gMoP3QGXO7/mf6hIQC/2iN7wd0peEQKFVvnJMp3gTVvi4eXfVd3DSa81MSzAVp7zVxXIiRnJTF0Kw==" } }, "fields": { "value": "one\ntwo\nthree\nfour\nfive" } }, { "type": "ImprovingU.Abstract", "hash": "ofFlc/jiNooYDrP1aKXsv15Ud0pBji+7k0KuZ4VneuKWCz02AHOi8yI+GZbIZZP9LShXf0mPxJgc2OKrA1zYPg==", "predecessors": { "idea": { "type": "ImprovingU.Idea", "hash": "xWNSMeJNeHxDsrRqT0ChoAwptn1uEn7D3pxUQcmxELydfufS8IylnoSXTMvoctaqIFJeoAF1zbDbi+mqtIuCbQ==" }, "prior": [{ "type": "ImprovingU.Abstract", "hash": "i+IaOkCvfL48ynvphvlarjGQHRNpIOyOlkxKCxEQH+EgRwcvw2e+Fb29QloU13Jg+R3Uz9ZaaVUCI3FakyzPEQ==" } ], "from": { "type": "Jinaga.User", "hash": "0WWlFbZH+gMoP3QGXO7/mf6hIQC/2iN7wd0peEQKFVvnJMp3gTVvi4eXfVd3DSa81MSzAVp7zVxXIiRnJTF0Kw==" } }, "fields": { "value": "one\ntwo" } }, { "type": "ImprovingU.Company", "hash": "aUdz/44uCMwjz2oJ/2Rw8poAJbmR1wrNTtnYiWHZv5puy/B43jcTmKXR0E0LbXe6bSKIM48uKVcicoR4afEauQ==", "predecessors": {}, "fields": { "name": "Improving" } }, { "type": "ImprovingU.Abstract", "hash": "i+IaOkCvfL48ynvphvlarjGQHRNpIOyOlkxKCxEQH+EgRwcvw2e+Fb29QloU13Jg+R3Uz9ZaaVUCI3FakyzPEQ==", "predecessors": { "idea": { "type": "ImprovingU.Idea", "hash": "xWNSMeJNeHxDsrRqT0ChoAwptn1uEn7D3pxUQcmxELydfufS8IylnoSXTMvoctaqIFJeoAF1zbDbi+mqtIuCbQ==" }, "prior": [], "from": { "type": "Jinaga.User", "hash": "0WWlFbZH+gMoP3QGXO7/mf6hIQC/2iN7wd0peEQKFVvnJMp3gTVvi4eXfVd3DSa81MSzAVp7zVxXIiRnJTF0Kw==" } }, "fields": { "value": "one" } }, { "type": "ImprovingU.Abstract", "hash": "Y3t5cOGrBG1/NmLL55HVT4qMpH808Wd5zw0Hz7ttTG3yR6g2nqISd8usOH67/4KsJ8DTXFsuzHK0DGqYMUMU9w==", "predecessors": { "idea": { "type": "ImprovingU.Idea", "hash": "xWNSMeJNeHxDsrRqT0ChoAwptn1uEn7D3pxUQcmxELydfufS8IylnoSXTMvoctaqIFJeoAF1zbDbi+mqtIuCbQ==" }, "prior": [{ "type": "ImprovingU.Abstract", "hash": "lXGr2y/MGnT283U5mF7oI+z/kZZ13kS2olxQD26vCCILIR5DmHG3orJcgNRkixmfuIUd7oGX9vdCwOZ+HGsgJg==" } ], "from": { "type": "Jinaga.User", "hash": "0WWlFbZH+gMoP3QGXO7/mf6hIQC/2iN7wd0peEQKFVvnJMp3gTVvi4eXfVd3DSa81MSzAVp7zVxXIiRnJTF0Kw==" } }, "fields": { "value": "one\ntwo\nthree\nfour\nfive\n\nsix" } }, { "type": "ImprovingU.Abstract", "hash": "Q473l768juQLIg1i3Lx/+6gtHKSRbyfY7GK5CzudvSKtMyhT6JxxUeYC6V5c3+uVWMAavnOsxXQPGX2ev/TVnw==", "predecessors": { "idea": { "type": "ImprovingU.Idea", "hash": "xWNSMeJNeHxDsrRqT0ChoAwptn1uEn7D3pxUQcmxELydfufS8IylnoSXTMvoctaqIFJeoAF1zbDbi+mqtIuCbQ==" }, "prior": [{ "type": "ImprovingU.Abstract", "hash": "gkCYyWW5BFEO+heluEKGpuYSFi7CdyXlF3AzH7DXeiR/GSGM5jIQpkOSazF1rcZXssdwqvrxaVmp1Qr58pu00g==" } ], "from": { "type": "Jinaga.User", "hash": "0WWlFbZH+gMoP3QGXO7/mf6hIQC/2iN7wd0peEQKFVvnJMp3gTVvi4eXfVd3DSa81MSzAVp7zVxXIiRnJTF0Kw==" } }, "fields": { "value": "one\ntwo\nthree\nfour" } }, { "type": "ImprovingU.Office", "hash": "jBdK3If9WDPqM5jMCJO02sHD2/03hNaXyFmQOXn4Q5vOBzYV5AkempdIGpirw/oaEGF9XJOZ9fkI1JzJKHeEhg==", "predecessors": { "company": { "type": "ImprovingU.Company", "hash": "aUdz/44uCMwjz2oJ/2Rw8poAJbmR1wrNTtnYiWHZv5puy/B43jcTmKXR0E0LbXe6bSKIM48uKVcicoR4afEauQ==" } }, "fields": { "name": "Dallas" } }, { "type": "ImprovingU.Semester", "hash": "xzRZ7MlatQrIpKaHAnmLRkN6D2PkTkMrlCvIpsISDae9Z0WAqdT+8fg3yTWsWoMLZ4snnmHQM6gQFC1SqZXFVw==", "predecessors": { "office": { "type": "ImprovingU.Office", "hash": "jBdK3If9WDPqM5jMCJO02sHD2/03hNaXyFmQOXn4Q5vOBzYV5AkempdIGpirw/oaEGF9XJOZ9fkI1JzJKHeEhg==" } }, "fields": { "name": "Fall 2018" } }, { "type": "Jinaga.User", "hash": "0WWlFbZH+gMoP3QGXO7/mf6hIQC/2iN7wd0peEQKFVvnJMp3gTVvi4eXfVd3DSa81MSzAVp7zVxXIiRnJTF0Kw==", "predecessors": {}, "fields": { "publicKey": "-----BEGIN RSA PUBLIC KEY-----\nMIGJAoGBAIBsKomutukULWw2zoTW2ECMrM8VmD2xvfpl3R4qh1whzuXV+A4EfRKMb/UAjEfw\n5nBmWvcObGyYUgygKrlNeOhf3MnDj706rej6ln9cKGL++ZNsJgJsogaAtmkPihWVGi908fdP\nLQrWTF5be0b/ZP258Zs3CTpcRTpTvhzS5TC1AgMBAAE=\n-----END RSA PUBLIC KEY-----\n" } }, { "type": "ImprovingU.Idea", "hash": "xWNSMeJNeHxDsrRqT0ChoAwptn1uEn7D3pxUQcmxELydfufS8IylnoSXTMvoctaqIFJeoAF1zbDbi+mqtIuCbQ==", "predecessors": { "semester": { "type": "ImprovingU.Semester", "hash": "xzRZ7MlatQrIpKaHAnmLRkN6D2PkTkMrlCvIpsISDae9Z0WAqdT+8fg3yTWsWoMLZ4snnmHQM6gQFC1SqZXFVw==" }, "from": { "type": "Jinaga.User", "hash": "0WWlFbZH+gMoP3QGXO7/mf6hIQC/2iN7wd0peEQKFVvnJMp3gTVvi4eXfVd3DSa81MSzAVp7zVxXIiRnJTF0Kw==" } }, "fields": { "createdAt": "2018-08-09T02:54:49.238Z", "title": "Topological order and you" } } ]; const sorter = givenTopologicalSorter(); const sorted = whenSort(sorter, facts); expect(sorter.finished()).toBe(true); const last = sorted[sorted.length-1]; expect((<any>last.fields).value).toEqual("one\ntwo\nthree\nfour\nfive\n\nsix"); }) }); function givenTopologicalSorter() { return new TopologicalSorter<FactRecord>(); } function whenSort(sorter: TopologicalSorter<FactRecord>, facts: FactRecord[]) { return sorter.sort(facts, (predecessors, fact) => fact); }