@opentermsarchive/engine 11.0.2 → 12.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.yaml +3 -0
- package/README.md +2 -0
- package/config/default.json +5 -0
- package/config/test.json +4 -1
- package/package.json +3 -2
- package/scripts/reporter/duplicate/index.js +1 -1
- package/src/archivist/collection/index.test.js +1 -1
- package/src/archivist/recorder/record.js +21 -0
- package/src/archivist/recorder/repositories/git/dataMapper.js +20 -11
- package/src/archivist/recorder/repositories/git/git.js +14 -2
- package/src/archivist/recorder/repositories/git/index.js +67 -11
- package/src/archivist/recorder/repositories/git/index.test.js +282 -6
- package/src/archivist/recorder/repositories/interface.js +44 -6
- package/src/archivist/recorder/repositories/mongo/index.js +67 -4
- package/src/archivist/recorder/repositories/mongo/index.test.js +330 -13
- package/src/archivist/recorder/version.test.js +38 -0
- package/src/archivist/services/index.js +1 -1
- package/src/collection-api/routes/feed.js +253 -0
- package/src/collection-api/routes/feed.test.js +739 -0
- package/src/collection-api/routes/index.js +16 -1
- package/src/collection-api/routes/services.js +1 -2
- package/src/collection-api/routes/services.test.js +4 -41
- package/src/collection-api/routes/versions.js +78 -78
- package/src/collection-api/routes/versions.test.js +4 -4
- package/src/collection-api/server.js +2 -0
- package/src/reporter/gitlab/index.js +1 -1
- package/src/reporter/index.js +7 -3
- package/src/reporter/index.test.js +62 -0
|
@@ -70,21 +70,59 @@ class RepositoryInterface {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
* Find all records
|
|
73
|
+
* Find all records, in descending chronological order (newest first; opposite of #iterate)
|
|
74
74
|
* For performance reasons, the content of the records will not be loaded by default. Use #loadRecordContent to load the content of individual records
|
|
75
|
-
* @see
|
|
76
|
-
* @
|
|
75
|
+
* @see RepositoryInterface#loadRecordContent
|
|
76
|
+
* @see RepositoryInterface#iterate
|
|
77
|
+
* @param {object} [options] - Query options
|
|
78
|
+
* @param {number} [options.limit] - Maximum number of records to return
|
|
79
|
+
* @param {number} [options.offset] - Number of records to skip
|
|
80
|
+
* @param {boolean} [options.includeTechnicalUpgrades] - When false, exclude technical upgrade records (re-renders of existing snapshots) and only return records that represent actual content changes. Default: true
|
|
81
|
+
* @returns {Promise<Array<Record>>} Promise that will be resolved with an array of records in descending chronological order
|
|
77
82
|
*/
|
|
78
|
-
async findAll() {
|
|
83
|
+
async findAll(options = {}) {
|
|
79
84
|
throw new Error(`#findAll method is not implemented in ${this.constructor.name}`);
|
|
80
85
|
}
|
|
81
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Find all records for a specific service, in descending chronological order
|
|
89
|
+
* For performance reasons, the content of the records will not be loaded by default. Use #loadRecordContent to load the content of individual records
|
|
90
|
+
* @see RepositoryInterface#loadRecordContent
|
|
91
|
+
* @param {string} serviceId - Service ID of records to find
|
|
92
|
+
* @param {object} [options] - Query options
|
|
93
|
+
* @param {number} [options.limit] - Maximum number of records to return
|
|
94
|
+
* @param {number} [options.offset] - Number of records to skip
|
|
95
|
+
* @param {boolean} [options.includeTechnicalUpgrades] - When false, exclude technical upgrade records (re-renders of existing snapshots) and only return records that represent actual content changes. Default: true
|
|
96
|
+
* @returns {Promise<Array<Record>>} Promise that will be resolved with an array of matching records in descending chronological order
|
|
97
|
+
*/
|
|
98
|
+
async findByService(serviceId, options = {}) {
|
|
99
|
+
throw new Error(`#findByService method is not implemented in ${this.constructor.name}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Find all records for a specific service and terms type, in descending chronological order
|
|
104
|
+
* For performance reasons, the content of the records will not be loaded by default. Use #loadRecordContent to load the content of individual records
|
|
105
|
+
* @see RepositoryInterface#loadRecordContent
|
|
106
|
+
* @param {string} serviceId - Service ID of records to find
|
|
107
|
+
* @param {string} termsType - Terms type of records to find
|
|
108
|
+
* @param {object} [options] - Query options
|
|
109
|
+
* @param {number} [options.limit] - Maximum number of records to return
|
|
110
|
+
* @param {number} [options.offset] - Number of records to skip
|
|
111
|
+
* @param {boolean} [options.includeTechnicalUpgrades] - When false, exclude technical upgrade records (re-renders of existing snapshots) and only return records that represent actual content changes. Default: true
|
|
112
|
+
* @returns {Promise<Array<Record>>} Promise that will be resolved with an array of matching records in descending chronological order
|
|
113
|
+
*/
|
|
114
|
+
async findByServiceAndTermsType(serviceId, termsType, options = {}) {
|
|
115
|
+
throw new Error(`#findByServiceAndTermsType method is not implemented in ${this.constructor.name}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
82
118
|
/**
|
|
83
119
|
* Count the total number of records in the repository
|
|
84
120
|
* For performance reasons, use this method rather than counting the number of entries returned by #findAll if you only need the size of a repository
|
|
85
|
-
* @
|
|
121
|
+
* @param {string} [serviceId] - Optional service ID to filter records
|
|
122
|
+
* @param {string} [termsType] - Optional terms type to filter records (requires serviceId)
|
|
123
|
+
* @returns {Promise<number>} Promise that will be resolved with the total number of records
|
|
86
124
|
*/
|
|
87
|
-
async count() {
|
|
125
|
+
async count(serviceId, termsType) {
|
|
88
126
|
throw new Error(`#count method is not implemented in ${this.constructor.name}`);
|
|
89
127
|
}
|
|
90
128
|
|
|
@@ -88,13 +88,76 @@ export default class MongoRepository extends RepositoryInterface {
|
|
|
88
88
|
return this.#toDomain(mongoDocument);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
async findAll() {
|
|
92
|
-
|
|
91
|
+
async findAll({ limit, offset, includeTechnicalUpgrades = true } = {}) {
|
|
92
|
+
const filter = includeTechnicalUpgrades ? {} : { isTechnicalUpgrade: { $ne: true } };
|
|
93
|
+
let query = this.collection.find(filter).project({ content: 0 }).sort({ fetchDate: -1 });
|
|
94
|
+
|
|
95
|
+
if (offset !== undefined) {
|
|
96
|
+
query = query.skip(offset);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (limit !== undefined) {
|
|
100
|
+
query = query.limit(limit);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return Promise.all((await query.toArray())
|
|
104
|
+
.map(mongoDocument => this.#toDomain(mongoDocument, { deferContentLoading: true })));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async findByServiceAndTermsType(serviceId, termsType, { limit, offset, includeTechnicalUpgrades = true } = {}) {
|
|
108
|
+
const filter = { serviceId, termsType };
|
|
109
|
+
|
|
110
|
+
if (!includeTechnicalUpgrades) {
|
|
111
|
+
filter.isTechnicalUpgrade = { $ne: true };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let query = this.collection.find(filter).project({ content: 0 }).sort({ fetchDate: -1 });
|
|
115
|
+
|
|
116
|
+
if (offset !== undefined) {
|
|
117
|
+
query = query.skip(offset);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (limit !== undefined) {
|
|
121
|
+
query = query.limit(limit);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return Promise.all((await query.toArray())
|
|
93
125
|
.map(mongoDocument => this.#toDomain(mongoDocument, { deferContentLoading: true })));
|
|
94
126
|
}
|
|
95
127
|
|
|
96
|
-
|
|
97
|
-
|
|
128
|
+
async findByService(serviceId, { limit, offset, includeTechnicalUpgrades = true } = {}) {
|
|
129
|
+
const filter = { serviceId };
|
|
130
|
+
|
|
131
|
+
if (!includeTechnicalUpgrades) {
|
|
132
|
+
filter.isTechnicalUpgrade = { $ne: true };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let query = this.collection.find(filter).project({ content: 0 }).sort({ fetchDate: -1 });
|
|
136
|
+
|
|
137
|
+
if (offset !== undefined) {
|
|
138
|
+
query = query.skip(offset);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (limit !== undefined) {
|
|
142
|
+
query = query.limit(limit);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return Promise.all((await query.toArray())
|
|
146
|
+
.map(mongoDocument => this.#toDomain(mongoDocument, { deferContentLoading: true })));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
count(serviceId, termsType) {
|
|
150
|
+
const filter = {};
|
|
151
|
+
|
|
152
|
+
if (serviceId) {
|
|
153
|
+
filter.serviceId = serviceId;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (termsType) {
|
|
157
|
+
filter.termsType = termsType;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return this.collection.countDocuments(filter);
|
|
98
161
|
}
|
|
99
162
|
|
|
100
163
|
async* iterate() {
|
|
@@ -629,45 +629,362 @@ describe('MongoRepository', () => {
|
|
|
629
629
|
}
|
|
630
630
|
});
|
|
631
631
|
|
|
632
|
-
it('returns records in
|
|
633
|
-
expect(records.map(record => record.fetchDate)).to.deep.equal([
|
|
632
|
+
it('returns records in descending order', () => {
|
|
633
|
+
expect(records.map(record => record.fetchDate)).to.deep.equal([ FETCH_DATE_LATER, FETCH_DATE, FETCH_DATE_EARLIER ]);
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
context('with includeTechnicalUpgrades: false', () => {
|
|
637
|
+
let filteredRecords;
|
|
638
|
+
|
|
639
|
+
before(async () => {
|
|
640
|
+
filteredRecords = await subject.findAll({ includeTechnicalUpgrades: false });
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
it('excludes technical upgrade records', () => {
|
|
644
|
+
expect(filteredRecords.length).to.equal(2);
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
it('only returns records that represent actual content changes', () => {
|
|
648
|
+
for (const record of filteredRecords) {
|
|
649
|
+
expect(record.isTechnicalUpgrade).to.not.be.true;
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
it('returns the expected records in descending order', () => {
|
|
654
|
+
expect(filteredRecords.map(record => record.fetchDate)).to.deep.equal([ FETCH_DATE_LATER, FETCH_DATE ]);
|
|
655
|
+
});
|
|
634
656
|
});
|
|
635
657
|
});
|
|
636
658
|
|
|
637
|
-
describe('#
|
|
638
|
-
|
|
659
|
+
describe('#findByServiceAndTermsType', () => {
|
|
660
|
+
const expectedIds = [];
|
|
661
|
+
let records;
|
|
639
662
|
|
|
640
663
|
before(async () => {
|
|
641
|
-
await subject.save(new Version({
|
|
664
|
+
const { id: id1 } = await subject.save(new Version({
|
|
642
665
|
serviceId: SERVICE_PROVIDER_ID,
|
|
643
666
|
termsType: TERMS_TYPE,
|
|
644
667
|
content: CONTENT,
|
|
645
668
|
fetchDate: FETCH_DATE,
|
|
646
669
|
snapshotIds: [SNAPSHOT_ID],
|
|
647
670
|
}));
|
|
648
|
-
|
|
671
|
+
|
|
672
|
+
expectedIds.push(id1);
|
|
673
|
+
|
|
674
|
+
const { id: id2 } = await subject.save(new Version({
|
|
649
675
|
serviceId: SERVICE_PROVIDER_ID,
|
|
650
676
|
termsType: TERMS_TYPE,
|
|
651
677
|
content: `${CONTENT} - updated`,
|
|
652
678
|
fetchDate: FETCH_DATE_LATER,
|
|
653
679
|
snapshotIds: [SNAPSHOT_ID],
|
|
654
680
|
}));
|
|
681
|
+
|
|
682
|
+
expectedIds.push(id2);
|
|
683
|
+
|
|
655
684
|
await subject.save(new Version({
|
|
685
|
+
serviceId: 'other_service',
|
|
686
|
+
termsType: 'Privacy Policy',
|
|
687
|
+
content: `${CONTENT} - other`,
|
|
688
|
+
fetchDate: FETCH_DATE,
|
|
689
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
690
|
+
}));
|
|
691
|
+
|
|
692
|
+
(records = await subject.findByServiceAndTermsType(SERVICE_PROVIDER_ID, TERMS_TYPE));
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
after(() => subject.removeAll());
|
|
696
|
+
|
|
697
|
+
it('returns only matching records', () => {
|
|
698
|
+
expect(records.length).to.equal(2);
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
it('returns Version objects', () => {
|
|
702
|
+
for (const record of records) {
|
|
703
|
+
expect(record).to.be.an.instanceof(Version);
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
it('returns records with matching service ID', () => {
|
|
708
|
+
for (const record of records) {
|
|
709
|
+
expect(record.serviceId).to.equal(SERVICE_PROVIDER_ID);
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
it('returns records with matching terms type', () => {
|
|
714
|
+
for (const record of records) {
|
|
715
|
+
expect(record.termsType).to.equal(TERMS_TYPE);
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
it('returns records in descending order', () => {
|
|
720
|
+
expect(records.map(record => record.fetchDate)).to.deep.equal([ FETCH_DATE_LATER, FETCH_DATE ]);
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
it('returns records with correct IDs', () => {
|
|
724
|
+
expect(records.map(record => record.id)).to.have.members(expectedIds);
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
context('when no matching records exist', () => {
|
|
728
|
+
it('returns an empty array', async () => {
|
|
729
|
+
const result = await subject.findByServiceAndTermsType('non_existent_service', 'Non Existent Terms');
|
|
730
|
+
|
|
731
|
+
expect(result).to.be.an('array').that.is.empty;
|
|
732
|
+
});
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
context('with includeTechnicalUpgrades: false', () => {
|
|
736
|
+
let filteredRecords;
|
|
737
|
+
let technicalUpgradeId;
|
|
738
|
+
|
|
739
|
+
before(async () => {
|
|
740
|
+
({ id: technicalUpgradeId } = await subject.save(new Version({
|
|
741
|
+
serviceId: SERVICE_PROVIDER_ID,
|
|
742
|
+
termsType: TERMS_TYPE,
|
|
743
|
+
content: `${CONTENT} - technical upgrade`,
|
|
744
|
+
fetchDate: FETCH_DATE_EARLIER,
|
|
745
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
746
|
+
isTechnicalUpgrade: true,
|
|
747
|
+
})));
|
|
748
|
+
|
|
749
|
+
filteredRecords = await subject.findByServiceAndTermsType(SERVICE_PROVIDER_ID, TERMS_TYPE, { includeTechnicalUpgrades: false });
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
it('excludes technical upgrade records', () => {
|
|
753
|
+
expect(filteredRecords.map(record => record.id)).to.not.include(technicalUpgradeId);
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
it('only returns records that represent actual content changes', () => {
|
|
757
|
+
for (const record of filteredRecords) {
|
|
758
|
+
expect(record.isTechnicalUpgrade).to.not.be.true;
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
describe('#findByService', () => {
|
|
765
|
+
const OTHER_TERMS_TYPE = 'Privacy Policy';
|
|
766
|
+
const expectedIds = [];
|
|
767
|
+
let records;
|
|
768
|
+
|
|
769
|
+
before(async () => {
|
|
770
|
+
const { id: id1 } = await subject.save(new Version({
|
|
656
771
|
serviceId: SERVICE_PROVIDER_ID,
|
|
657
772
|
termsType: TERMS_TYPE,
|
|
658
|
-
content:
|
|
659
|
-
|
|
773
|
+
content: CONTENT,
|
|
774
|
+
fetchDate: FETCH_DATE,
|
|
775
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
776
|
+
}));
|
|
777
|
+
|
|
778
|
+
expectedIds.push(id1);
|
|
779
|
+
|
|
780
|
+
const { id: id2 } = await subject.save(new Version({
|
|
781
|
+
serviceId: SERVICE_PROVIDER_ID,
|
|
782
|
+
termsType: TERMS_TYPE,
|
|
783
|
+
content: `${CONTENT} - updated`,
|
|
784
|
+
fetchDate: FETCH_DATE_LATER,
|
|
785
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
786
|
+
}));
|
|
787
|
+
|
|
788
|
+
expectedIds.push(id2);
|
|
789
|
+
|
|
790
|
+
const { id: id3 } = await subject.save(new Version({
|
|
791
|
+
serviceId: SERVICE_PROVIDER_ID,
|
|
792
|
+
termsType: OTHER_TERMS_TYPE,
|
|
793
|
+
content: `${CONTENT} - other terms type`,
|
|
660
794
|
fetchDate: FETCH_DATE_EARLIER,
|
|
661
795
|
snapshotIds: [SNAPSHOT_ID],
|
|
662
796
|
}));
|
|
663
797
|
|
|
664
|
-
|
|
798
|
+
expectedIds.push(id3);
|
|
799
|
+
|
|
800
|
+
await subject.save(new Version({
|
|
801
|
+
serviceId: 'other_service',
|
|
802
|
+
termsType: TERMS_TYPE,
|
|
803
|
+
content: `${CONTENT} - other service`,
|
|
804
|
+
fetchDate: FETCH_DATE,
|
|
805
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
806
|
+
}));
|
|
807
|
+
|
|
808
|
+
(records = await subject.findByService(SERVICE_PROVIDER_ID));
|
|
665
809
|
});
|
|
666
810
|
|
|
667
811
|
after(() => subject.removeAll());
|
|
668
812
|
|
|
669
|
-
it('returns
|
|
670
|
-
expect(
|
|
813
|
+
it('returns only matching records', () => {
|
|
814
|
+
expect(records.length).to.equal(3);
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
it('returns Version objects', () => {
|
|
818
|
+
for (const record of records) {
|
|
819
|
+
expect(record).to.be.an.instanceof(Version);
|
|
820
|
+
}
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
it('returns records with matching service ID', () => {
|
|
824
|
+
for (const record of records) {
|
|
825
|
+
expect(record.serviceId).to.equal(SERVICE_PROVIDER_ID);
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
it('returns records across all terms types of the service', () => {
|
|
830
|
+
expect(new Set(records.map(record => record.termsType))).to.deep.equal(new Set([ TERMS_TYPE, OTHER_TERMS_TYPE ]));
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
it('returns records in descending order', () => {
|
|
834
|
+
expect(records.map(record => record.fetchDate)).to.deep.equal([ FETCH_DATE_LATER, FETCH_DATE, FETCH_DATE_EARLIER ]);
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
it('returns records with correct IDs', () => {
|
|
838
|
+
expect(records.map(record => record.id)).to.have.members(expectedIds);
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
context('when no matching records exist', () => {
|
|
842
|
+
it('returns an empty array', async () => {
|
|
843
|
+
const result = await subject.findByService('non_existent_service');
|
|
844
|
+
|
|
845
|
+
expect(result).to.be.an('array').that.is.empty;
|
|
846
|
+
});
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
context('with includeTechnicalUpgrades: false', () => {
|
|
850
|
+
let filteredRecords;
|
|
851
|
+
let technicalUpgradeId;
|
|
852
|
+
|
|
853
|
+
before(async () => {
|
|
854
|
+
({ id: technicalUpgradeId } = await subject.save(new Version({
|
|
855
|
+
serviceId: SERVICE_PROVIDER_ID,
|
|
856
|
+
termsType: TERMS_TYPE,
|
|
857
|
+
content: `${CONTENT} - technical upgrade`,
|
|
858
|
+
fetchDate: new Date('2000-01-03T12:00:00.000Z'),
|
|
859
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
860
|
+
isTechnicalUpgrade: true,
|
|
861
|
+
})));
|
|
862
|
+
|
|
863
|
+
filteredRecords = await subject.findByService(SERVICE_PROVIDER_ID, { includeTechnicalUpgrades: false });
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
it('excludes technical upgrade records', () => {
|
|
867
|
+
expect(filteredRecords.map(record => record.id)).to.not.include(technicalUpgradeId);
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
it('only returns records that represent actual content changes', () => {
|
|
871
|
+
for (const record of filteredRecords) {
|
|
872
|
+
expect(record.isTechnicalUpgrade).to.not.be.true;
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
});
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
describe('#count', () => {
|
|
879
|
+
context('without filters', () => {
|
|
880
|
+
let count;
|
|
881
|
+
|
|
882
|
+
before(async () => {
|
|
883
|
+
await subject.save(new Version({
|
|
884
|
+
serviceId: SERVICE_PROVIDER_ID,
|
|
885
|
+
termsType: TERMS_TYPE,
|
|
886
|
+
content: CONTENT,
|
|
887
|
+
fetchDate: FETCH_DATE,
|
|
888
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
889
|
+
}));
|
|
890
|
+
await subject.save(new Version({
|
|
891
|
+
serviceId: SERVICE_PROVIDER_ID,
|
|
892
|
+
termsType: TERMS_TYPE,
|
|
893
|
+
content: `${CONTENT} - updated`,
|
|
894
|
+
fetchDate: FETCH_DATE_LATER,
|
|
895
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
896
|
+
}));
|
|
897
|
+
await subject.save(new Version({
|
|
898
|
+
serviceId: SERVICE_PROVIDER_ID,
|
|
899
|
+
termsType: TERMS_TYPE,
|
|
900
|
+
content: `${CONTENT} - updated 2`,
|
|
901
|
+
isTechnicalUpgrade: true,
|
|
902
|
+
fetchDate: FETCH_DATE_EARLIER,
|
|
903
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
904
|
+
}));
|
|
905
|
+
|
|
906
|
+
(count = await subject.count());
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
after(() => subject.removeAll());
|
|
910
|
+
|
|
911
|
+
it('returns the proper count', () => {
|
|
912
|
+
expect(count).to.equal(3);
|
|
913
|
+
});
|
|
914
|
+
});
|
|
915
|
+
|
|
916
|
+
context('with serviceId and termsType filters', () => {
|
|
917
|
+
before(async () => {
|
|
918
|
+
await subject.save(new Version({
|
|
919
|
+
serviceId: SERVICE_PROVIDER_ID,
|
|
920
|
+
termsType: TERMS_TYPE,
|
|
921
|
+
content: CONTENT,
|
|
922
|
+
fetchDate: FETCH_DATE,
|
|
923
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
924
|
+
}));
|
|
925
|
+
await subject.save(new Version({
|
|
926
|
+
serviceId: SERVICE_PROVIDER_ID,
|
|
927
|
+
termsType: TERMS_TYPE,
|
|
928
|
+
content: `${CONTENT} - updated`,
|
|
929
|
+
fetchDate: FETCH_DATE_LATER,
|
|
930
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
931
|
+
}));
|
|
932
|
+
await subject.save(new Version({
|
|
933
|
+
serviceId: 'other_service',
|
|
934
|
+
termsType: 'Privacy Policy',
|
|
935
|
+
content: 'Other content',
|
|
936
|
+
fetchDate: FETCH_DATE,
|
|
937
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
938
|
+
}));
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
after(() => subject.removeAll());
|
|
942
|
+
|
|
943
|
+
it('returns count for specific service and terms type', async () => {
|
|
944
|
+
const filteredCount = await subject.count(SERVICE_PROVIDER_ID, TERMS_TYPE);
|
|
945
|
+
|
|
946
|
+
expect(filteredCount).to.equal(2);
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
it('returns zero for non-existent service', async () => {
|
|
950
|
+
const filteredCount = await subject.count('non-existent-service', TERMS_TYPE);
|
|
951
|
+
|
|
952
|
+
expect(filteredCount).to.equal(0);
|
|
953
|
+
});
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
context('with only serviceId filter', () => {
|
|
957
|
+
before(async () => {
|
|
958
|
+
await subject.save(new Version({
|
|
959
|
+
serviceId: SERVICE_PROVIDER_ID,
|
|
960
|
+
termsType: TERMS_TYPE,
|
|
961
|
+
content: CONTENT,
|
|
962
|
+
fetchDate: FETCH_DATE,
|
|
963
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
964
|
+
}));
|
|
965
|
+
await subject.save(new Version({
|
|
966
|
+
serviceId: SERVICE_PROVIDER_ID,
|
|
967
|
+
termsType: 'Different Terms',
|
|
968
|
+
content: 'Different content',
|
|
969
|
+
fetchDate: FETCH_DATE_LATER,
|
|
970
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
971
|
+
}));
|
|
972
|
+
await subject.save(new Version({
|
|
973
|
+
serviceId: 'other_service',
|
|
974
|
+
termsType: 'Privacy Policy',
|
|
975
|
+
content: 'Other content',
|
|
976
|
+
fetchDate: FETCH_DATE,
|
|
977
|
+
snapshotIds: [SNAPSHOT_ID],
|
|
978
|
+
}));
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
after(() => subject.removeAll());
|
|
982
|
+
|
|
983
|
+
it('returns count for all terms types of a service', async () => {
|
|
984
|
+
const filteredCount = await subject.count(SERVICE_PROVIDER_ID);
|
|
985
|
+
|
|
986
|
+
expect(filteredCount).to.equal(2);
|
|
987
|
+
});
|
|
671
988
|
});
|
|
672
989
|
});
|
|
673
990
|
|
|
@@ -1197,8 +1514,8 @@ describe('MongoRepository', () => {
|
|
|
1197
1514
|
}
|
|
1198
1515
|
});
|
|
1199
1516
|
|
|
1200
|
-
it('returns records in
|
|
1201
|
-
expect(records.map(record => record.fetchDate)).to.deep.equal([
|
|
1517
|
+
it('returns records in descending order', () => {
|
|
1518
|
+
expect(records.map(record => record.fetchDate)).to.deep.equal([ FETCH_DATE_LATER, FETCH_DATE, FETCH_DATE_EARLIER ]);
|
|
1202
1519
|
});
|
|
1203
1520
|
});
|
|
1204
1521
|
|
|
@@ -60,4 +60,42 @@ describe('Version', () => {
|
|
|
60
60
|
});
|
|
61
61
|
});
|
|
62
62
|
});
|
|
63
|
+
|
|
64
|
+
describe('#displayTitle', () => {
|
|
65
|
+
const baseParams = {
|
|
66
|
+
serviceId: 'service-A',
|
|
67
|
+
termsType: 'Terms of Service',
|
|
68
|
+
fetchDate: new Date('2000-01-01T12:00:00.000Z'),
|
|
69
|
+
content: 'some content',
|
|
70
|
+
snapshotIds: ['dd263f270b3065e1c18201b49ab898474b357566'],
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
context('when the record is the first one for its service and terms type', () => {
|
|
74
|
+
it('starts with the first-record prefix', () => {
|
|
75
|
+
subject = new Version({ ...baseParams, isFirstRecord: true });
|
|
76
|
+
expect(subject.displayTitle).to.equal('First record of service-A Terms of Service');
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
context('when the record is a technical upgrade', () => {
|
|
81
|
+
it('starts with the technical-upgrade prefix', () => {
|
|
82
|
+
subject = new Version({ ...baseParams, isTechnicalUpgrade: true });
|
|
83
|
+
expect(subject.displayTitle).to.equal('Apply technical or declaration upgrade on service-A Terms of Service');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
context('when the record is a regular content change', () => {
|
|
88
|
+
it('starts with the update prefix', () => {
|
|
89
|
+
subject = new Version(baseParams);
|
|
90
|
+
expect(subject.displayTitle).to.equal('Record new changes of service-A Terms of Service');
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
context('when the record is both a first record and a technical upgrade', () => {
|
|
95
|
+
it('prioritises the first-record prefix', () => {
|
|
96
|
+
subject = new Version({ ...baseParams, isFirstRecord: true, isTechnicalUpgrade: true });
|
|
97
|
+
expect(subject.displayTitle).to.equal('First record of service-A Terms of Service');
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
63
101
|
});
|
|
@@ -281,7 +281,7 @@ function getHistoryFilePaths(serviceId) {
|
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
async function loadServiceHistory(historyFilePath) {
|
|
284
|
-
if (!(await fileExists(historyFilePath))) return {};
|
|
284
|
+
if (!(await fileExists(historyFilePath))) { return {}; }
|
|
285
285
|
|
|
286
286
|
try {
|
|
287
287
|
return JSON.parse(await fs.readFile(historyFilePath));
|