fix(products): restore NA low-voltage cables broken by flattened frontmatter
Commit 76cbdaa3 destroyed the YAML indentation of NAYY, NAYCWY, NAY2Y,
NA2XY and NA2X2Y, silently dropping them from the catalog (404 detail
pages, missing from category listing and sitemap). Restored the valid
frontmatter from 76cbdaa3~1 (whitespace-only diff, content unchanged)
and added a content contract spec guarding parseability and catalog
visibility.
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
# Incident Ledger
|
||||
|
||||
One appended line per real incident. Never rewritten.
|
||||
|
||||
| Date | Area | Mechanism | Root cause | Remedy | Tier |
|
||||
| :--------- | :------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--- |
|
||||
| 2026-10-09 | Products / Niederspannungskabel | Five aluminium low-voltage products (NAYY, NAYCWY, NAY2Y, NA2XY, NA2X2Y) silently invisible on the site: dropped from category listing, 404 on detail pages, absent from sitemap | Commit 76cbdaa3 (2026-08-07, release 2.3.33 "NA Kabel Leiterklassen Fix") flattened frontmatter indentation of exactly these five files while removing RE/SE conductor shapes; broken YAML made the product loader return null and the site dropped them without any error | Restored valid frontmatter from 76cbdaa3~1 (content verified identical modulo indentation, whitespace-only diff), kept current markdown body incl. the RE/SE fix; regression spec tests/products-content.test.ts guards parseability and catalog visibility | M |
|
||||
@@ -0,0 +1,71 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import matter from 'gray-matter';
|
||||
import { getAllProducts } from '../lib/products';
|
||||
|
||||
const productsDir = path.join(process.cwd(), 'content', 'products');
|
||||
const productFiles = fs.readdirSync(productsDir).filter((f) => f.endsWith('.mdx'));
|
||||
|
||||
interface CatalogEntry {
|
||||
slug: string;
|
||||
parseable: boolean;
|
||||
error?: string;
|
||||
declaresImages: boolean;
|
||||
}
|
||||
|
||||
// Single parse pass at module load, mirroring the production loader's first
|
||||
// touch of each file (gray-matter caches per input string, so re-parsing the
|
||||
// same file inside individual tests would not re-throw parse errors).
|
||||
const catalog: CatalogEntry[] = productFiles.map((file) => {
|
||||
const raw = fs.readFileSync(path.join(productsDir, file), 'utf-8');
|
||||
try {
|
||||
const { data } = matter(raw);
|
||||
return {
|
||||
slug: file.replace('.mdx', ''),
|
||||
parseable: true,
|
||||
declaresImages: Array.isArray(data.images) && data.images.length > 0,
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
slug: file.replace('.mdx', ''),
|
||||
parseable: false,
|
||||
error: `${file}: ${(error as Error).message.split('\n')[0]}`,
|
||||
// An unparseable file cannot declare anything and must never silently
|
||||
// shrink the expected catalog — require it to be listed.
|
||||
declaresImages: true,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const ALUMINIUM_LOW_VOLTAGE_SLUGS = ['nayy', 'naycwy', 'nay2y', 'na2xy', 'na2x2y'];
|
||||
|
||||
describe('Product catalog content contract', () => {
|
||||
it('every product file has parseable frontmatter', () => {
|
||||
expect(catalog.filter((entry) => !entry.parseable).map((e) => e.error)).toEqual([]);
|
||||
});
|
||||
|
||||
it('exposes every product that declares images in the catalog listing', async () => {
|
||||
const expected = catalog
|
||||
.filter((entry) => entry.declaresImages)
|
||||
.map((e) => e.slug)
|
||||
.sort();
|
||||
const actual = (await getAllProducts('de')).map((p) => p.slug).sort();
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('lists all aluminium low-voltage cable types (NAYY, NAYCWY, NAY2Y, NA2XY, NA2X2Y)', async () => {
|
||||
const slugs = (await getAllProducts('de')).map((p) => p.slug);
|
||||
for (const slug of ALUMINIUM_LOW_VOLTAGE_SLUGS) {
|
||||
expect(slugs).toContain(slug);
|
||||
}
|
||||
});
|
||||
|
||||
it('categorizes the aluminium low-voltage types as Niederspannungskabel', async () => {
|
||||
const products = await getAllProducts('de');
|
||||
for (const slug of ALUMINIUM_LOW_VOLTAGE_SLUGS) {
|
||||
const product = products.find((p) => p.slug === slug);
|
||||
expect(product?.frontmatter.categories[0]).toBe('Niederspannungskabel');
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user