Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

All changes that impact users of this module are documented in this file, in the [Common Changelog](https://common-changelog.org) format with some additional specifications defined in the CONTRIBUTING file. This codebase adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased [patch]

> Development of this release was made on a volunteer basis by [be-student](https://github.com/be-student).

### Fixed

- Check duplicate source document locations during schema-only declaration validation

## 15.1.0 - 2026-07-13

> Development of this release was supported by [User Rights](https://www.user-rights.org).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "Duplicate source documents",
"terms": {
"Privacy Policy": {
"combine": [
{
"fetch": "https://example.com/privacy",
"select": "main"
},
{
"fetch": "https://example.com/privacy",
"select": "article"
}
]
}
}
}
24 changes: 16 additions & 8 deletions scripts/declarations/validate/index.mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,22 @@ export default async options => {
}
});

if (service) {
service.getTermsTypes()
.filter(type => service.terms[type]?.latest)
.forEach(type => {
const terms = service.getTerms({ type });

if (terms.hasMultipleSourceDocuments) {
it(`does not declare the same source document more than once within "${type}"`, () => {
const duplicateLocations = [...new Set(terms.duplicateSourceDocuments.map(sourceDocument => sourceDocument.location))];

expect(duplicateLocations, `The same source document is declared more than once within the "${type}" combine: ${duplicateLocations.join(', ')}`).to.be.empty;
});
}
});
}

if (!schemaOnly && service) {
service.getTermsTypes()
.filter(termsType => {
Expand All @@ -118,14 +134,6 @@ export default async options => {
describe(type, () => {
const terms = service.getTerms({ type });

if (terms.hasMultipleSourceDocuments) {
it('does not declare the same source document more than once', () => {
const duplicateLocations = [...new Set(terms.duplicateSourceDocuments.map(sourceDocument => sourceDocument.location))];

expect(duplicateLocations, `The same source document is declared more than once within the "${type}" combine: ${duplicateLocations.join(', ')}`).to.be.empty;
});
}

terms.sourceDocuments.forEach(sourceDocument => {
let filteredContent;

Expand Down
29 changes: 29 additions & 0 deletions scripts/declarations/validate/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { spawnSync } from 'child_process';
import path from 'path';
import { fileURLToPath } from 'url';

import { expect } from 'chai';

describe('Declaration validation command', () => {
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const collectionPath = path.join(__dirname, 'fixtures', 'duplicate-collection');

it('rejects duplicate source documents during schema-only validation', () => {
const result = spawnSync(
process.execPath,
[ 'bin/ota.js', 'validate', 'declarations', '--schema-only' ],
{
cwd: process.cwd(),
encoding: 'utf8',
env: {
...process.env,
NODE_CONFIG: JSON.stringify({ '@opentermsarchive/engine': { collectionPath } }),
NODE_ENV: 'test',
},
},
);

expect(result.status).to.equal(1);
expect(result.stdout + result.stderr).to.include('The same source document is declared more than once within the "Privacy Policy" combine: https://example.com/privacy');
}).timeout(10000);
});