feat: complete MDX migration for blog, fix diagram fidelity and refactor styling architecture
This commit is contained in:
@@ -1,311 +0,0 @@
|
||||
/**
|
||||
* Test the integration between blog posts and file examples
|
||||
* This simulates what happens when a blog post is rendered
|
||||
*/
|
||||
|
||||
import { blogPosts } from "../data/blogPosts";
|
||||
import { FileExampleManager } from "../data/fileExamples";
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
export async function testBlogPostIntegration() {
|
||||
console.log("🧪 Testing Blog Post + File Examples Integration...\n");
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
const test = (name: string, fn: () => void | Promise<void>) => {
|
||||
try {
|
||||
const result = fn();
|
||||
if (result instanceof Promise) {
|
||||
return result
|
||||
.then(() => {
|
||||
console.log(`✅ ${name}`);
|
||||
passed++;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(`❌ ${name}`);
|
||||
console.error(` Error: ${err.message}`);
|
||||
failed++;
|
||||
});
|
||||
} else {
|
||||
console.log(`✅ ${name}`);
|
||||
passed++;
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.log(`❌ ${name}`);
|
||||
console.error(` Error: ${err.message}`);
|
||||
failed++;
|
||||
}
|
||||
};
|
||||
|
||||
// Test 1: Blog posts exist
|
||||
test("Blog posts are loaded", () => {
|
||||
if (!blogPosts || blogPosts.length === 0) {
|
||||
throw new Error("No blog posts found");
|
||||
}
|
||||
console.log(` Found ${blogPosts.length} posts`);
|
||||
});
|
||||
|
||||
// Test 2: Each post has required fields
|
||||
test("All posts have required fields", () => {
|
||||
for (const post of blogPosts) {
|
||||
if (!post.slug || !post.title || !post.tags) {
|
||||
throw new Error(`Post ${post.slug} missing required fields`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Test 3: Debugging-tips post should have file examples
|
||||
test("debugging-tips post has file examples", async () => {
|
||||
const post = blogPosts.find((p) => p.slug === "debugging-tips");
|
||||
if (!post) {
|
||||
throw new Error("debugging-tips post not found");
|
||||
}
|
||||
|
||||
// Check if it would trigger file examples
|
||||
const _showFileExamples = post.tags?.some((tag) =>
|
||||
[
|
||||
"architecture",
|
||||
"design-patterns",
|
||||
"system-design",
|
||||
"docker",
|
||||
"deployment",
|
||||
].includes(tag),
|
||||
);
|
||||
|
||||
// debugging-tips has tags ['debugging', 'tools'] so showFileExamples would be false
|
||||
// But it has hardcoded FileExamplesList in the template
|
||||
|
||||
// Verify files exist for this post
|
||||
const groups = await FileExampleManager.getAllGroups();
|
||||
const filesForPost = groups
|
||||
.flatMap((g) => g.files)
|
||||
.filter((f) => f.postSlug === "debugging-tips");
|
||||
|
||||
if (filesForPost.length === 0) {
|
||||
throw new Error("No files found for debugging-tips");
|
||||
}
|
||||
|
||||
console.log(` Found ${filesForPost.length} files for debugging-tips`);
|
||||
});
|
||||
|
||||
// Test 4: Architecture-patterns post should have file examples
|
||||
test("architecture-patterns post has file examples", async () => {
|
||||
const post = blogPosts.find((p) => p.slug === "architecture-patterns");
|
||||
if (!post) {
|
||||
throw new Error("architecture-patterns post not found");
|
||||
}
|
||||
|
||||
// Check if it would trigger file examples
|
||||
const showFileExamples = post.tags?.some((tag) =>
|
||||
[
|
||||
"architecture",
|
||||
"design-patterns",
|
||||
"system-design",
|
||||
"docker",
|
||||
"deployment",
|
||||
].includes(tag),
|
||||
);
|
||||
|
||||
if (!showFileExamples) {
|
||||
throw new Error("architecture-patterns should show file examples");
|
||||
}
|
||||
|
||||
// Verify files exist for this post
|
||||
const groups = await FileExampleManager.getAllGroups();
|
||||
const filesForPost = groups
|
||||
.flatMap((g) => g.files)
|
||||
.filter((f) => f.postSlug === "architecture-patterns");
|
||||
|
||||
if (filesForPost.length === 0) {
|
||||
throw new Error("No files found for architecture-patterns");
|
||||
}
|
||||
|
||||
console.log(
|
||||
` Found ${filesForPost.length} files for architecture-patterns`,
|
||||
);
|
||||
});
|
||||
|
||||
// Test 5: Docker-deployment post should have file examples
|
||||
test("docker-deployment post has file examples", async () => {
|
||||
const post = blogPosts.find((p) => p.slug === "docker-deployment");
|
||||
if (!post) {
|
||||
throw new Error("docker-deployment post not found");
|
||||
}
|
||||
|
||||
// Check if it would trigger file examples
|
||||
const showFileExamples = post.tags?.some((tag) =>
|
||||
[
|
||||
"architecture",
|
||||
"design-patterns",
|
||||
"system-design",
|
||||
"docker",
|
||||
"deployment",
|
||||
].includes(tag),
|
||||
);
|
||||
|
||||
if (!showFileExamples) {
|
||||
throw new Error("docker-deployment should show file examples");
|
||||
}
|
||||
|
||||
// Verify files exist for this post
|
||||
const groups = await FileExampleManager.getAllGroups();
|
||||
const filesForPost = groups
|
||||
.flatMap((g) => g.files)
|
||||
.filter((f) => f.postSlug === "docker-deployment");
|
||||
|
||||
if (filesForPost.length === 0) {
|
||||
throw new Error("No files found for docker-deployment");
|
||||
}
|
||||
|
||||
console.log(` Found ${filesForPost.length} files for docker-deployment`);
|
||||
});
|
||||
|
||||
// Test 6: First-note post should NOT have file examples
|
||||
test("first-note post has no file examples", async () => {
|
||||
const post = blogPosts.find((p) => p.slug === "first-note");
|
||||
if (!post) {
|
||||
throw new Error("first-note post not found");
|
||||
}
|
||||
|
||||
// Check if it would trigger file examples
|
||||
const showFileExamples = post.tags?.some((tag) =>
|
||||
[
|
||||
"architecture",
|
||||
"design-patterns",
|
||||
"system-design",
|
||||
"docker",
|
||||
"deployment",
|
||||
].includes(tag),
|
||||
);
|
||||
|
||||
if (showFileExamples) {
|
||||
throw new Error("first-note should NOT show file examples");
|
||||
}
|
||||
|
||||
// Verify no files exist for this post
|
||||
const groups = await FileExampleManager.getAllGroups();
|
||||
const filesForPost = groups
|
||||
.flatMap((g) => g.files)
|
||||
.filter((f) => f.postSlug === "first-note");
|
||||
|
||||
if (filesForPost.length > 0) {
|
||||
throw new Error("Files found for first-note, but none should exist");
|
||||
}
|
||||
|
||||
console.log(` Correctly has no files`);
|
||||
});
|
||||
|
||||
// Test 7: Simulate FileExamplesList filtering for debugging-tips
|
||||
test("FileExamplesList filtering works for debugging-tips", async () => {
|
||||
const postSlug = "debugging-tips";
|
||||
const groupId = "python-data-processing";
|
||||
|
||||
const allGroups = await FileExampleManager.getAllGroups();
|
||||
const loadedGroups = allGroups
|
||||
.filter((g) => g.groupId === groupId)
|
||||
.map((g) => ({
|
||||
...g,
|
||||
files: g.files.filter((f) => f.postSlug === postSlug),
|
||||
}))
|
||||
.filter((g) => g.files.length > 0);
|
||||
|
||||
if (loadedGroups.length === 0) {
|
||||
throw new Error(
|
||||
"No groups loaded for debugging-tips with python-data-processing",
|
||||
);
|
||||
}
|
||||
|
||||
if (loadedGroups[0].files.length === 0) {
|
||||
throw new Error("No files in the group");
|
||||
}
|
||||
|
||||
console.log(` Would show ${loadedGroups[0].files.length} files`);
|
||||
});
|
||||
|
||||
// Test 8: Simulate FileExamplesList filtering for architecture-patterns
|
||||
test("FileExamplesList filtering works for architecture-patterns", async () => {
|
||||
const postSlug = "architecture-patterns";
|
||||
const tags = ["architecture", "design-patterns", "system-design"];
|
||||
|
||||
const allGroups = await FileExampleManager.getAllGroups();
|
||||
const loadedGroups = allGroups
|
||||
.map((g) => ({
|
||||
...g,
|
||||
files: g.files.filter((f) => {
|
||||
if (f.postSlug !== postSlug) return false;
|
||||
if (tags && tags.length > 0) {
|
||||
return f.tags?.some((tag) => tags.includes(tag));
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
}))
|
||||
.filter((g) => g.files.length > 0);
|
||||
|
||||
if (loadedGroups.length === 0) {
|
||||
throw new Error("No groups loaded for architecture-patterns");
|
||||
}
|
||||
|
||||
const totalFiles = loadedGroups.reduce((sum, g) => sum + g.files.length, 0);
|
||||
if (totalFiles === 0) {
|
||||
throw new Error("No files found");
|
||||
}
|
||||
|
||||
console.log(
|
||||
` Would show ${totalFiles} files across ${loadedGroups.length} groups`,
|
||||
);
|
||||
});
|
||||
|
||||
// Test 9: Verify all file examples have postSlug
|
||||
test("All file examples have postSlug property", async () => {
|
||||
const groups = await FileExampleManager.getAllGroups();
|
||||
const filesWithoutPostSlug = groups
|
||||
.flatMap((g) => g.files)
|
||||
.filter((f) => !f.postSlug);
|
||||
|
||||
if (filesWithoutPostSlug.length > 0) {
|
||||
throw new Error(`${filesWithoutPostSlug.length} files missing postSlug`);
|
||||
}
|
||||
|
||||
console.log(
|
||||
` All ${groups.flatMap((g) => g.files).length} files have postSlug`,
|
||||
);
|
||||
});
|
||||
|
||||
// Test 10: Verify postSlugs match blog post slugs
|
||||
test("File example postSlugs match blog post slugs", async () => {
|
||||
const groups = await FileExampleManager.getAllGroups();
|
||||
const filePostSlugs = new Set(
|
||||
groups.flatMap((g) => g.files).map((f) => f.postSlug),
|
||||
);
|
||||
const blogPostSlugs = new Set(blogPosts.map((p) => p.slug));
|
||||
|
||||
for (const slug of filePostSlugs) {
|
||||
if (slug && !blogPostSlugs.has(slug)) {
|
||||
throw new Error(`File postSlug "${slug}" doesn't match any blog post`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(` All file postSlugs match blog posts`);
|
||||
});
|
||||
|
||||
// Wait for async tests
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
console.log(
|
||||
`\n📊 Integration Test Results: ${passed} passed, ${failed} failed`,
|
||||
);
|
||||
|
||||
if (failed === 0) {
|
||||
console.log("🎉 All integration tests passed!");
|
||||
console.log(
|
||||
"\n✅ The file examples system is correctly integrated with blog posts!",
|
||||
);
|
||||
} else {
|
||||
console.log("❌ Some integration tests failed");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Export for use in other test files
|
||||
@@ -1,283 +0,0 @@
|
||||
/**
|
||||
* Comprehensive tests for the file examples system
|
||||
*/
|
||||
|
||||
import {
|
||||
FileExampleManager,
|
||||
sampleFileExamples,
|
||||
type FileExample as _FileExample,
|
||||
} from "../data/fileExamples";
|
||||
|
||||
// Test helper to run all tests
|
||||
export async function runFileExamplesTests() {
|
||||
console.log("🧪 Running File Examples System Tests...\n");
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
const test = (name: string, fn: () => void | Promise<void>) => {
|
||||
try {
|
||||
const result = fn();
|
||||
if (result instanceof Promise) {
|
||||
return result
|
||||
.then(() => {
|
||||
console.log(`✅ ${name}`);
|
||||
passed++;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(`❌ ${name}`);
|
||||
console.error(` Error: ${err.message}`);
|
||||
failed++;
|
||||
});
|
||||
} else {
|
||||
console.log(`✅ ${name}`);
|
||||
passed++;
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.log(`❌ ${name}`);
|
||||
console.error(` Error: ${err.message}`);
|
||||
failed++;
|
||||
}
|
||||
};
|
||||
|
||||
// Test 1: Data structure exists
|
||||
test("File examples data is loaded", () => {
|
||||
if (!sampleFileExamples || sampleFileExamples.length === 0) {
|
||||
throw new Error("No file examples found");
|
||||
}
|
||||
console.log(` Found ${sampleFileExamples.length} groups`);
|
||||
});
|
||||
|
||||
// Test 2: FileExampleManager exists
|
||||
test("FileExampleManager class is available", () => {
|
||||
if (!FileExampleManager) {
|
||||
throw new Error("FileExampleManager not found");
|
||||
}
|
||||
});
|
||||
|
||||
// Test 3: Sample data has correct structure
|
||||
test("Sample data has correct structure", () => {
|
||||
const group = sampleFileExamples[0];
|
||||
if (!group.groupId || !group.title || !Array.isArray(group.files)) {
|
||||
throw new Error("Invalid group structure");
|
||||
}
|
||||
|
||||
const file = group.files[0];
|
||||
if (!file.id || !file.filename || !file.content || !file.language) {
|
||||
throw new Error("Invalid file structure");
|
||||
}
|
||||
|
||||
// Check for postSlug
|
||||
if (!file.postSlug) {
|
||||
throw new Error("Files missing postSlug property");
|
||||
}
|
||||
});
|
||||
|
||||
// Test 4: Get all groups
|
||||
test("FileExampleManager.getAllGroups() works", async () => {
|
||||
const groups = await FileExampleManager.getAllGroups();
|
||||
if (!Array.isArray(groups) || groups.length === 0) {
|
||||
throw new Error("getAllGroups returned invalid result");
|
||||
}
|
||||
});
|
||||
|
||||
// Test 5: Get specific group
|
||||
test("FileExampleManager.getGroup() works", async () => {
|
||||
const group = await FileExampleManager.getGroup("python-data-processing");
|
||||
if (!group) {
|
||||
throw new Error("Group not found");
|
||||
}
|
||||
if (group.groupId !== "python-data-processing") {
|
||||
throw new Error("Wrong group returned");
|
||||
}
|
||||
});
|
||||
|
||||
// Test 6: Search files
|
||||
test("FileExampleManager.searchFiles() works", async () => {
|
||||
const results = await FileExampleManager.searchFiles("python");
|
||||
if (!Array.isArray(results)) {
|
||||
throw new Error("searchFiles returned invalid result");
|
||||
}
|
||||
if (results.length === 0) {
|
||||
throw new Error('No results found for "python"');
|
||||
}
|
||||
});
|
||||
|
||||
// Test 7: Get file by ID
|
||||
test("FileExampleManager.getFileExample() works", async () => {
|
||||
const file = await FileExampleManager.getFileExample(
|
||||
"python-data-processor",
|
||||
);
|
||||
if (!file) {
|
||||
throw new Error("File not found");
|
||||
}
|
||||
if (file.id !== "python-data-processor") {
|
||||
throw new Error("Wrong file returned");
|
||||
}
|
||||
});
|
||||
|
||||
// Test 8: Filter by postSlug
|
||||
test("Filter files by postSlug", async () => {
|
||||
const groups = await FileExampleManager.getAllGroups();
|
||||
const debuggingFiles = groups
|
||||
.flatMap((g) => g.files)
|
||||
.filter((f) => f.postSlug === "debugging-tips");
|
||||
|
||||
if (debuggingFiles.length === 0) {
|
||||
throw new Error("No files found for debugging-tips");
|
||||
}
|
||||
|
||||
console.log(` Found ${debuggingFiles.length} files for debugging-tips`);
|
||||
});
|
||||
|
||||
// Test 9: Filter by postSlug and groupId
|
||||
test("Filter files by postSlug and groupId", async () => {
|
||||
const groups = await FileExampleManager.getAllGroups();
|
||||
const filtered = groups
|
||||
.filter((g) => g.groupId === "python-data-processing")
|
||||
.map((g) => ({
|
||||
...g,
|
||||
files: g.files.filter((f) => f.postSlug === "debugging-tips"),
|
||||
}))
|
||||
.filter((g) => g.files.length > 0);
|
||||
|
||||
if (filtered.length === 0) {
|
||||
throw new Error(
|
||||
"No files found for debugging-tips in python-data-processing",
|
||||
);
|
||||
}
|
||||
|
||||
console.log(` Found ${filtered[0].files.length} files`);
|
||||
});
|
||||
|
||||
// Test 10: Filter by postSlug and tags
|
||||
test("Filter files by postSlug and tags", async () => {
|
||||
const groups = await FileExampleManager.getAllGroups();
|
||||
const tags = ["architecture", "design-patterns"];
|
||||
|
||||
const filtered = groups
|
||||
.map((g) => ({
|
||||
...g,
|
||||
files: g.files.filter(
|
||||
(f) =>
|
||||
f.postSlug === "architecture-patterns" &&
|
||||
f.tags?.some((tag) => tags.includes(tag)),
|
||||
),
|
||||
}))
|
||||
.filter((g) => g.files.length > 0);
|
||||
|
||||
if (filtered.length === 0) {
|
||||
throw new Error("No files found for architecture-patterns with tags");
|
||||
}
|
||||
|
||||
console.log(` Found ${filtered[0].files.length} files`);
|
||||
});
|
||||
|
||||
// Test 11: Download single file
|
||||
test("Download single file", async () => {
|
||||
const result = await FileExampleManager.downloadFile(
|
||||
"python-data-processor",
|
||||
);
|
||||
if (!result) {
|
||||
throw new Error("Download failed");
|
||||
}
|
||||
if (!result.filename || !result.content || !result.mimeType) {
|
||||
throw new Error("Invalid download result");
|
||||
}
|
||||
});
|
||||
|
||||
// Test 12: Download multiple files
|
||||
test("Download multiple files", async () => {
|
||||
const files = await FileExampleManager.downloadMultiple([
|
||||
"python-data-processor",
|
||||
"python-config-example",
|
||||
]);
|
||||
if (!Array.isArray(files) || files.length !== 2) {
|
||||
throw new Error("Invalid multiple download result");
|
||||
}
|
||||
});
|
||||
|
||||
// Test 13: Get available tags
|
||||
test("Get available tags", async () => {
|
||||
const tags = await FileExampleManager.getAvailableTags();
|
||||
if (!Array.isArray(tags) || tags.length === 0) {
|
||||
throw new Error("No tags found");
|
||||
}
|
||||
if (!tags.includes("python") || !tags.includes("architecture")) {
|
||||
throw new Error("Expected tags not found");
|
||||
}
|
||||
});
|
||||
|
||||
// Test 14: Create new file example
|
||||
test("Create new file example", async () => {
|
||||
const newExample = await FileExampleManager.createFileExample({
|
||||
filename: "test.py",
|
||||
content: 'print("test")',
|
||||
language: "python",
|
||||
description: "Test file",
|
||||
tags: ["test"],
|
||||
postSlug: "test-post",
|
||||
});
|
||||
|
||||
if (!newExample.id) {
|
||||
throw new Error("New example has no ID");
|
||||
}
|
||||
|
||||
// Verify it was added
|
||||
const retrieved = await FileExampleManager.getFileExample(newExample.id);
|
||||
if (!retrieved || retrieved.filename !== "test.py") {
|
||||
throw new Error("New example not found");
|
||||
}
|
||||
});
|
||||
|
||||
// Test 15: Update file example
|
||||
test("Update file example", async () => {
|
||||
const updated = await FileExampleManager.updateFileExample(
|
||||
"python-data-processor",
|
||||
{
|
||||
description: "Updated description",
|
||||
},
|
||||
);
|
||||
|
||||
if (!updated || updated.description !== "Updated description") {
|
||||
throw new Error("Update failed");
|
||||
}
|
||||
});
|
||||
|
||||
// Test 16: Delete file example
|
||||
test("Delete file example", async () => {
|
||||
// First create one
|
||||
const created = await FileExampleManager.createFileExample({
|
||||
filename: "delete-test.py",
|
||||
content: "test",
|
||||
language: "python",
|
||||
postSlug: "test",
|
||||
});
|
||||
|
||||
// Then delete it
|
||||
const deleted = await FileExampleManager.deleteFileExample(created.id);
|
||||
if (!deleted) {
|
||||
throw new Error("Delete failed");
|
||||
}
|
||||
|
||||
// Verify it's gone
|
||||
const retrieved = await FileExampleManager.getFileExample(created.id);
|
||||
if (retrieved) {
|
||||
throw new Error("File still exists after deletion");
|
||||
}
|
||||
});
|
||||
|
||||
// Wait for all async tests to complete
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
console.log(`\n📊 Test Results: ${passed} passed, ${failed} failed`);
|
||||
|
||||
if (failed === 0) {
|
||||
console.log("🎉 All tests passed!");
|
||||
} else {
|
||||
console.log("❌ Some tests failed");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Export for use in other test files
|
||||
Reference in New Issue
Block a user