Files
google-drive-content-adapter/tests/unit/proxy-sitemap.test.js.old

387 lines
12 KiB
JavaScript

/**
* Unit Tests: Sitemap Generation Logic
*
* Tests sitemap XML generation functions
* Tests T028, T029, T030
*/
import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
describe('Unit: escapeXml() (T028)', () => {
// Mock XML escape function (will be in proxy.js)
function escapeXml(str) {
if (typeof str !== 'string') return '';
return str
.replace(/&/g, '&')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&apos;');
}
it('T028: should escape < character to &lt;', () => {
// Given: String with < character
const input = 'test < value';
// When: Escaping for XML
const output = escapeXml(input);
// Then: Should escape <
assert.equal(output, 'test &lt; value', 'Should escape <');
});
it('T028: should escape > character to &gt;', () => {
// Given: String with > character
const input = 'test > value';
// When: Escaping for XML
const output = escapeXml(input);
// Then: Should escape >
assert.equal(output, 'test &gt; value', 'Should escape >');
});
it('T028: should escape & character to &amp;', () => {
// Given: String with & character
const input = 'test & value';
// When: Escaping for XML
const output = escapeXml(input);
// Then: Should escape &
assert.equal(output, 'test &amp; value', 'Should escape &');
});
it('T028: should escape " character to &quot;', () => {
// Given: String with " character
const input = 'test "value"';
// When: Escaping for XML
const output = escapeXml(input);
// Then: Should escape "
assert.equal(output, 'test &quot;value&quot;', 'Should escape "');
});
it('T028: should escape \' character to &apos;', () => {
// Given: String with ' character
const input = "test 'value'";
// When: Escaping for XML
const output = escapeXml(input);
// Then: Should escape '
assert.equal(output, 'test &apos;value&apos;', 'Should escape \'');
});
it('T028: should escape multiple special characters in correct order', () => {
// Given: String with multiple special characters
const input = '<tag attr="value" other=\'test\'>content & more</tag>';
// When: Escaping for XML
const output = escapeXml(input);
// Then: Should escape all characters properly
assert.equal(
output,
'&lt;tag attr=&quot;value&quot; other=&apos;test&apos;&gt;content &amp; more&lt;/tag&gt;',
'Should escape all XML special characters'
);
});
it('T028: should handle strings without special characters', () => {
// Given: String without special characters
const input = 'normal text 123';
// When: Escaping for XML
const output = escapeXml(input);
// Then: Should return unchanged
assert.equal(output, input, 'Should not modify strings without special chars');
});
it('T028: should handle empty string', () => {
// Given: Empty string
const input = '';
// When: Escaping for XML
const output = escapeXml(input);
// Then: Should return empty string
assert.equal(output, '', 'Should handle empty string');
});
it('T028: should handle non-string input gracefully', () => {
// Given: Non-string inputs
const inputs = [null, undefined, 123, { foo: 'bar' }];
// When: Escaping each input
// Then: Should return empty string for non-strings
inputs.forEach(input => {
const output = escapeXml(input);
assert.equal(output, '', `Should return empty string for ${typeof input}`);
});
});
});
describe('Unit: formatSitemapEntry() (T029)', () => {
// Mock sitemap entry formatter (will be in proxy.js)
function formatSitemapEntry(document, baseUrl) {
function escapeXml(str) {
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
const loc = `${baseUrl}/${document.id}`;
const lastmod = document.modifiedTime;
return ` <url>
<loc>${escapeXml(loc)}</loc>
<lastmod>${lastmod}</lastmod>
</url>`;
}
it('T029: should convert DriveDocument to XML url element', () => {
// Given: DriveDocument metadata
const document = {
id: '1BxAA_test123',
name: 'Test Document',
modifiedTime: '2026-03-06T10:30:00Z'
};
const baseUrl = 'http://localhost:3000';
// When: Formatting sitemap entry
const xml = formatSitemapEntry(document, baseUrl);
// Then: Should generate valid XML
assert.ok(xml.includes('<url>'), 'Should contain opening url tag');
assert.ok(xml.includes('</url>'), 'Should contain closing url tag');
assert.ok(xml.includes('<loc>'), 'Should contain loc element');
assert.ok(xml.includes('</loc>'), 'Should contain closing loc tag');
assert.ok(xml.includes('<lastmod>'), 'Should contain lastmod element');
assert.ok(xml.includes('</lastmod>'), 'Should contain closing lastmod tag');
});
it('T029: should include correct location URL with documentId', () => {
// Given: DriveDocument metadata
const document = {
id: '1BxAA_test123',
name: 'Test Document',
modifiedTime: '2026-03-06T10:30:00Z'
};
const baseUrl = 'http://localhost:3000';
// When: Formatting sitemap entry
const xml = formatSitemapEntry(document, baseUrl);
// Then: Location should point to adapter endpoint
assert.ok(
xml.includes(`<loc>http://localhost:3000/${document.id}</loc>`),
'Should include correct location URL'
);
});
it('T029: should include ISO 8601 lastmod timestamp', () => {
// Given: DriveDocument with modified time
const document = {
id: '1BxAA_test123',
name: 'Test Document',
modifiedTime: '2026-03-06T10:30:00Z'
};
const baseUrl = 'http://localhost:3000';
// When: Formatting sitemap entry
const xml = formatSitemapEntry(document, baseUrl);
// Then: Should include lastmod with ISO 8601 timestamp
assert.ok(
xml.includes('<lastmod>2026-03-06T10:30:00Z</lastmod>'),
'Should include ISO 8601 lastmod timestamp'
);
});
it('T029: should escape special XML characters in URL', () => {
// Given: DriveDocument with special characters in ID (edge case)
const document = {
id: '1BxAA-test&123',
name: 'Test Document',
modifiedTime: '2026-03-06T10:30:00Z'
};
const baseUrl = 'http://localhost:3000';
// When: Formatting sitemap entry
const xml = formatSitemapEntry(document, baseUrl);
// Then: Should escape & in URL
assert.ok(
xml.includes('&amp;'),
'Should escape special XML characters in URL'
);
});
it('T029: should handle different baseUrl formats', () => {
// Given: Different baseUrl formats
const document = {
id: '1BxAA_test',
name: 'Test',
modifiedTime: '2026-03-06T10:30:00Z'
};
const baseUrls = [
'http://localhost:3000',
'https://example.com',
'https://api.example.com/v1'
];
// When: Formatting with each baseUrl
// Then: Should generate correct loc for each
baseUrls.forEach(baseUrl => {
const xml = formatSitemapEntry(document, baseUrl);
assert.ok(
xml.includes(`<loc>${baseUrl}/${document.id}</loc>`),
`Should work with baseUrl: ${baseUrl}`
);
});
});
});
describe('Unit: generateSitemap() Structure (T030)', () => {
// Mock sitemap generator structure (will be in proxy.js)
function buildSitemapXml(documents, baseUrl) {
function escapeXml(str) {
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
xml += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n';
documents.forEach(doc => {
const loc = `${baseUrl}/${doc.id}`;
xml += ` <url>\n`;
xml += ` <loc>${escapeXml(loc)}</loc>\n`;
xml += ` <lastmod>${doc.modifiedTime}</lastmod>\n`;
xml += ` </url>\n`;
});
xml += '</urlset>';
return xml;
}
it('T030: should build complete XML with declaration', () => {
// Given: Array of documents
const documents = [
{ id: '1BxAA_doc1', name: 'Doc 1', modifiedTime: '2026-03-06T10:00:00Z' }
];
const baseUrl = 'http://localhost:3000';
// When: Building sitemap XML
const xml = buildSitemapXml(documents, baseUrl);
// Then: Should start with XML declaration
assert.ok(
xml.startsWith('<?xml version="1.0"'),
'Should start with XML declaration'
);
});
it('T030: should include correct sitemap namespace', () => {
// Given: Array of documents
const documents = [
{ id: '1BxAA_doc1', name: 'Doc 1', modifiedTime: '2026-03-06T10:00:00Z' }
];
const baseUrl = 'http://localhost:3000';
// When: Building sitemap XML
const xml = buildSitemapXml(documents, baseUrl);
// Then: Should include sitemap protocol namespace
assert.ok(
xml.includes('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'),
'Should include correct sitemap namespace'
);
});
it('T030: should include closing urlset tag', () => {
// Given: Array of documents
const documents = [
{ id: '1BxAA_doc1', name: 'Doc 1', modifiedTime: '2026-03-06T10:00:00Z' }
];
const baseUrl = 'http://localhost:3000';
// When: Building sitemap XML
const xml = buildSitemapXml(documents, baseUrl);
// Then: Should end with closing urlset tag
assert.ok(xml.endsWith('</urlset>'), 'Should end with closing urlset tag');
});
it('T030: should include multiple url entries for multiple documents', () => {
// Given: Multiple documents
const documents = [
{ id: '1BxAA_doc1', name: 'Doc 1', modifiedTime: '2026-03-06T10:00:00Z' },
{ id: '2CyBB_doc2', name: 'Doc 2', modifiedTime: '2026-03-06T11:00:00Z' },
{ id: '3DzCC_doc3', name: 'Doc 3', modifiedTime: '2026-03-06T12:00:00Z' }
];
const baseUrl = 'http://localhost:3000';
// When: Building sitemap XML
const xml = buildSitemapXml(documents, baseUrl);
// Then: Should include all documents
const urlCount = (xml.match(/<url>/g) || []).length;
assert.equal(urlCount, 3, 'Should include 3 url entries');
// Then: Each document should have its loc
documents.forEach(doc => {
assert.ok(
xml.includes(`<loc>http://localhost:3000/${doc.id}</loc>`),
`Should include url entry for ${doc.id}`
);
});
});
it('T030: should handle empty document list', () => {
// Given: Empty documents array
const documents = [];
const baseUrl = 'http://localhost:3000';
// When: Building sitemap XML
const xml = buildSitemapXml(documents, baseUrl);
// Then: Should still have valid XML structure
assert.ok(xml.includes('<?xml version'), 'Should have XML declaration');
assert.ok(xml.includes('<urlset'), 'Should have urlset opening');
assert.ok(xml.includes('</urlset>'), 'Should have urlset closing');
// Then: Should have no url entries
const urlCount = (xml.match(/<url>/g) || []).length;
assert.equal(urlCount, 0, 'Should have no url entries');
});
it('T030: should generate valid XML that browsers can parse', () => {
// Given: Sample documents
const documents = [
{ id: '1BxAA_test', name: 'Test', modifiedTime: '2026-03-06T10:00:00Z' }
];
const baseUrl = 'http://localhost:3000';
// When: Building sitemap XML
const xml = buildSitemapXml(documents, baseUrl);
// Then: XML should be well-formed (basic checks)
// Count opening and closing tags
const openingUrlset = (xml.match(/<urlset/g) || []).length;
const closingUrlset = (xml.match(/<\/urlset>/g) || []).length;
assert.equal(openingUrlset, closingUrlset, 'urlset tags should be balanced');
const openingUrl = (xml.match(/<url>/g) || []).length;
const closingUrl = (xml.match(/<\/url>/g) || []).length;
assert.equal(openingUrl, closingUrl, 'url tags should be balanced');
});
});