205 lines
5.8 KiB
JavaScript
205 lines
5.8 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Download official EN16931 and PEPPOL test samples for conformance testing
|
|
*/
|
|
|
|
import * as https from 'https';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import { createWriteStream } from 'fs';
|
|
import { pipeline } from 'stream/promises';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
interface TestSampleSource {
|
|
name: string;
|
|
description: string;
|
|
repository: string;
|
|
branch: string;
|
|
paths: string[];
|
|
targetDir: string;
|
|
}
|
|
|
|
const TEST_SAMPLE_SOURCES: TestSampleSource[] = [
|
|
{
|
|
name: 'PEPPOL BIS 3.0 Examples',
|
|
description: 'Official PEPPOL BIS Billing 3.0 example files',
|
|
repository: 'OpenPEPPOL/peppol-bis-invoice-3',
|
|
branch: 'master',
|
|
paths: [
|
|
'rules/examples/Allowance-example.xml',
|
|
'rules/examples/base-example.xml',
|
|
'rules/examples/base-negative-inv-correction.xml',
|
|
'rules/examples/vat-category-E.xml',
|
|
'rules/examples/vat-category-O.xml',
|
|
'rules/examples/vat-category-S.xml',
|
|
'rules/examples/vat-category-Z.xml',
|
|
'rules/examples/vat-category-AE.xml',
|
|
'rules/examples/vat-category-K.xml',
|
|
'rules/examples/vat-category-G.xml'
|
|
],
|
|
targetDir: 'peppol-bis3'
|
|
},
|
|
{
|
|
name: 'CEN TC434 Test Files',
|
|
description: 'European Committee for Standardization test files',
|
|
repository: 'ConnectingEurope/eInvoicing-EN16931',
|
|
branch: 'master',
|
|
paths: [
|
|
'ubl/examples/ubl-tc434-example1.xml',
|
|
'ubl/examples/ubl-tc434-example2.xml',
|
|
'ubl/examples/ubl-tc434-example3.xml',
|
|
'ubl/examples/ubl-tc434-example4.xml',
|
|
'ubl/examples/ubl-tc434-example5.xml',
|
|
'ubl/examples/ubl-tc434-example6.xml',
|
|
'ubl/examples/ubl-tc434-example7.xml',
|
|
'ubl/examples/ubl-tc434-example8.xml',
|
|
'ubl/examples/ubl-tc434-example9.xml',
|
|
'cii/examples/cii-tc434-example1.xml',
|
|
'cii/examples/cii-tc434-example2.xml',
|
|
'cii/examples/cii-tc434-example3.xml',
|
|
'cii/examples/cii-tc434-example4.xml',
|
|
'cii/examples/cii-tc434-example5.xml',
|
|
'cii/examples/cii-tc434-example6.xml',
|
|
'cii/examples/cii-tc434-example7.xml',
|
|
'cii/examples/cii-tc434-example8.xml',
|
|
'cii/examples/cii-tc434-example9.xml'
|
|
],
|
|
targetDir: 'cen-tc434'
|
|
},
|
|
{
|
|
name: 'PEPPOL Validation Artifacts',
|
|
description: 'PEPPOL validation test files',
|
|
repository: 'OpenPEPPOL/peppol-bis-invoice-3',
|
|
branch: 'master',
|
|
paths: [
|
|
'rules/unit-UBL/PEPPOL-EN16931-UBL.xml'
|
|
],
|
|
targetDir: 'peppol-validation'
|
|
}
|
|
];
|
|
|
|
/**
|
|
* Download a file from GitHub
|
|
*/
|
|
async function downloadFile(
|
|
repo: string,
|
|
branch: string,
|
|
filePath: string,
|
|
targetPath: string
|
|
): Promise<void> {
|
|
const url = `https://raw.githubusercontent.com/${repo}/${branch}/${filePath}`;
|
|
|
|
return new Promise((resolve, reject) => {
|
|
https.get(url, (response) => {
|
|
if (response.statusCode === 404) {
|
|
console.warn(` ⚠️ File not found: ${filePath}`);
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
if (response.statusCode !== 200) {
|
|
reject(new Error(`Failed to download ${url}: ${response.statusCode}`));
|
|
return;
|
|
}
|
|
|
|
const dir = path.dirname(targetPath);
|
|
if (!fs.existsSync(dir)) {
|
|
fs.mkdirSync(dir, { recursive: true });
|
|
}
|
|
|
|
const file = createWriteStream(targetPath);
|
|
response.pipe(file);
|
|
|
|
file.on('finish', () => {
|
|
file.close();
|
|
console.log(` ✅ Downloaded: ${path.basename(filePath)}`);
|
|
resolve();
|
|
});
|
|
|
|
file.on('error', (err) => {
|
|
fs.unlink(targetPath, () => {}); // Delete incomplete file
|
|
reject(err);
|
|
});
|
|
}).on('error', reject);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Download test samples from a source
|
|
*/
|
|
async function downloadTestSamples(source: TestSampleSource): Promise<void> {
|
|
console.log(`\n📦 ${source.name}`);
|
|
console.log(` ${source.description}`);
|
|
console.log(` Repository: ${source.repository}`);
|
|
|
|
const baseDir = path.join('test-samples', source.targetDir);
|
|
|
|
for (const filePath of source.paths) {
|
|
const fileName = path.basename(filePath);
|
|
const targetPath = path.join(baseDir, fileName);
|
|
|
|
try {
|
|
await downloadFile(source.repository, source.branch, filePath, targetPath);
|
|
} catch (error) {
|
|
console.error(` ❌ Error downloading ${fileName}: ${error.message}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create metadata file for downloaded samples
|
|
*/
|
|
function createMetadata(sources: TestSampleSource[]): void {
|
|
const metadata = {
|
|
downloadDate: new Date().toISOString(),
|
|
sources: sources.map(s => ({
|
|
name: s.name,
|
|
repository: s.repository,
|
|
branch: s.branch,
|
|
fileCount: s.paths.length
|
|
})),
|
|
totalFiles: sources.reduce((sum, s) => sum + s.paths.length, 0)
|
|
};
|
|
|
|
const metadataPath = path.join('test-samples', 'metadata.json');
|
|
fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2));
|
|
console.log('\n📝 Created metadata.json');
|
|
}
|
|
|
|
/**
|
|
* Main function
|
|
*/
|
|
async function main() {
|
|
console.log('🚀 Downloading official EN16931 test samples...\n');
|
|
|
|
// Create base directory
|
|
if (!fs.existsSync('test-samples')) {
|
|
fs.mkdirSync('test-samples');
|
|
}
|
|
|
|
// Download samples from each source
|
|
for (const source of TEST_SAMPLE_SOURCES) {
|
|
await downloadTestSamples(source);
|
|
}
|
|
|
|
// Create metadata file
|
|
createMetadata(TEST_SAMPLE_SOURCES);
|
|
|
|
console.log('\n✨ Test sample download complete!');
|
|
console.log('📁 Samples saved to: test-samples/');
|
|
|
|
// Count total files
|
|
const totalFiles = TEST_SAMPLE_SOURCES.reduce((sum, s) => sum + s.paths.length, 0);
|
|
console.log(`📊 Total files: ${totalFiles}`);
|
|
}
|
|
|
|
// Run if executed directly
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
main().catch(console.error);
|
|
}
|
|
|
|
export { downloadTestSamples, TEST_SAMPLE_SOURCES }; |