This commit is contained in:
2025-12-01 19:53:51 +01:00
parent 7dba940d80
commit 88680f1954
4 changed files with 253 additions and 373 deletions

View File

@@ -234,80 +234,81 @@ pub fn (mut c DocTreeClient) has_errors(collection_name string) bool {
pub fn (mut c DocTreeClient) copy_collection(collection_name string, destination_path string) ! { pub fn (mut c DocTreeClient) copy_collection(collection_name string, destination_path string) ! {
// TODO: list over all pages, links & files and copy them to destination // TODO: list over all pages, links & files and copy them to destination
} }
// will copy all pages linked from a page to a destination directory as well as the page itself // // will copy all pages linked from a page to a destination directory as well as the page itself
pub fn (mut c DocTreeClient) copy_pages(collection_name string, page_name string, destination_path string) ! { // pub fn (mut c DocTreeClient) copy_pages(collection_name string, page_name string, destination_path string) ! {
// TODO: copy page itself // // TODO: copy page itself
// Get page links from metadata // // Get page links from metadata
links := c.get_page_links(collection_name, page_name)! // links := c.get_page_links(collection_name, page_name)!
// Create img subdirectory // // Create img subdirectory
mut img_dest := pathlib.get_dir(path: '${destination_path}', create: true)! // mut img_dest := pathlib.get_dir(path: '${destination_path}', create: true)!
// Copy only image links // // Copy only image links
for link in links { // for link in links {
if link.file_type != .page { // if link.file_type != .page {
continue // continue
} // }
if link.status == .external { // if link.status == .external {
continue // continue
} // }
// Get image path and copy // // Get image path and copy
img_path := c.get_page_path(link.target_collection_name, link.target_item_name)! // img_path := c.get_page_path(link.target_collection_name, link.target_item_name)!
mut src := pathlib.get_file(path: img_path)! // mut src := pathlib.get_file(path: img_path)!
src.copy(dest: '${img_dest.path}/${src.name_fix_no_ext()}')! // src.copy(dest: '${img_dest.path}/${src.name_fix_no_ext()}')!
console.print_debug(' ********. Copied page: ${src.path} to ${img_dest.path}/${src.name_fix_no_ext()}') // console.print_debug(' ********. Copied page: ${src.path} to ${img_dest.path}/${src.name_fix_no_ext()}')
} // }
} // }
pub fn (mut c DocTreeClient) copy_images(collection_name string, page_name string, destination_path string) ! { // pub fn (mut c DocTreeClient) copy_images(collection_name string, page_name string, destination_path string) ! {
// Get page links from metadata // // Get page links from metadata
links := c.get_page_links(collection_name, page_name)! // links := c.get_page_links(collection_name, page_name)!
// Create img subdirectory // // Create img subdirectory
mut img_dest := pathlib.get_dir(path: '${destination_path}/img', create: true)! // mut img_dest := pathlib.get_dir(path: '${destination_path}/img', create: true)!
// Copy only image links // // Copy only image links
for link in links { // for link in links {
if link.file_type != .image { // if link.file_type != .image {
continue // continue
} // }
if link.status == .external { // if link.status == .external {
continue // continue
} // }
// Get image path and copy // // Get image path and copy
img_path := c.get_image_path(link.target_collection_name, link.target_item_name)! // img_path := c.get_image_path(link.target_collection_name, link.target_item_name)!
mut src := pathlib.get_file(path: img_path)! // mut src := pathlib.get_file(path: img_path)!
src.copy(dest: '${img_dest.path}/${src.name_fix_no_ext()}')! // src.copy(dest: '${img_dest.path}/${src.name_fix_no_ext()}')!
// console.print_debug('Copied image: ${src.path} to ${img_dest.path}/${src.name_fix()}') // // console.print_debug('Copied image: ${src.path} to ${img_dest.path}/${src.name_fix()}')
} // }
} // }
// copy_files copies all non-image files from a page to a destination directory // // copy_files copies all non-image files from a page to a destination directory
// Files are placed in {destination}/files/ subdirectory // // Files are placed in {destination}/files/ subdirectory
// Only copies files referenced in the page (via links) // // Only copies files referenced in the page (via links)
pub fn (mut c DocTreeClient) copy_files(collection_name string, page_name string, destination_path string) ! { // pub fn (mut c DocTreeClient) copy_files(collection_name string, page_name string, destination_path string) ! {
// Get page links from metadata // // Get page links from metadata
links := c.get_page_links(collection_name, page_name)! // links := c.get_page_links(collection_name, page_name)!
// Create files subdirectory // // Create files subdirectory
mut files_dest := pathlib.get_dir(path: '${destination_path}/files', create: true)! // mut files_dest := pathlib.get_dir(path: '${destination_path}/files', create: true)!
// Copy only file links (non-image files) // // Copy only file links (non-image files)
for link in links { // for link in links {
if link.file_type != .file { // if link.file_type != .file {
continue // continue
} // }
if link.status == .external { // if link.status == .external {
continue // continue
} // }
// println(link) // // println(link)
// Get file path and copy // // Get file path and copy
file_path := c.get_file_path(link.target_collection_name, link.target_item_name)! // file_path := c.get_file_path(link.target_collection_name, link.target_item_name)!
mut src := pathlib.get_file(path: file_path)! // mut src := pathlib.get_file(path: file_path)!
// src.copy(dest: '${files_dest.path}/${src.name_fix_no_ext()}')! // // src.copy(dest: '${files_dest.path}/${src.name_fix_no_ext()}')!
console.print_debug('Copied file: ${src.path} to ${files_dest.path}/${src.name_fix_no_ext()}') // console.print_debug('Copied file: ${src.path} to ${files_dest.path}/${src.name_fix_no_ext()}')
} // }
} // }

View File

@@ -1,119 +0,0 @@
module client
import incubaid.herolib.core.pathlib
import incubaid.herolib.core.texttools
import incubaid.herolib.ui.console
import os
import json
import incubaid.herolib.core.redisclient
// get_page_links returns all links found in a page and pages linked to it (recursive)
// This includes transitive links through page-to-page references
// External links, files, and images do not recurse further
pub fn (mut c DocTreeClient) get_page_links(collection_name string, page_name string) ![]LinkMetadata {
mut visited := map[string]bool{}
mut all_links := []LinkMetadata{}
c.collect_page_links_recursive(collection_name, page_name, mut visited, mut all_links)!
return all_links
}
// collect_page_links_recursive is the internal recursive implementation
// It traverses all linked pages and collects all links found
//
// Thread safety: Each call to get_page_links gets its own visited map
// Circular references are prevented by tracking visited pages
//
// Link types behavior:
// - .page links: Recursively traverse to get links from the target page
// - .file and .image links: Included in results but not recursively expanded
// - .external links: Included in results but not recursively expanded
fn (mut c DocTreeClient) collect_page_links_recursive(collection_name string, page_name string, mut visited map[string]bool, mut all_links []LinkMetadata) ! {
// Create unique key for cycle detection
page_key := '${collection_name}:${page_name}'
// Prevent infinite loops on circular page references
// Example: Page A Page B Page A
if page_key in visited {
return
}
visited[page_key] = true
// Get collection metadata
metadata := c.get_collection_metadata(collection_name)!
fixed_page_name := texttools.name_fix(page_name)
// Find the page in metadata
if fixed_page_name !in metadata.pages {
return error('page_not_found: Page "${page_name}" not found in collection metadata, for collection: "${collection_name}"')
}
page_meta := metadata.pages[fixed_page_name]
// Add all direct links from this page to the result
// This includes: pages, files, images, and external links
all_links << page_meta.links
// Recursively traverse only page-to-page links
for link in page_meta.links {
// Only recursively process links to other pages within the doctree
// Skip external links (http, https, mailto, etc.)
// Skip file and image links (these don't have "contained" links)
if link.file_type != .page || link.status == .external {
continue
}
// Recursively collect links from the target page
c.collect_page_links_recursive(link.target_collection_name, link.target_item_name, mut
visited, mut all_links) or {
// If we encounter an error (e.g., target page doesn't exist in metadata),
// we continue processing other links rather than failing completely
// This provides graceful degradation for broken link references
continue
}
}
}
// get_image_links returns all image links found in a page and related pages (recursive)
// This is a convenience function that filters get_page_links to only image links
pub fn (mut c DocTreeClient) get_image_links(collection_name string, page_name string) ![]LinkMetadata {
all_links := c.get_page_links(collection_name, page_name)!
mut image_links := []LinkMetadata{}
for link in all_links {
if link.file_type == .image {
image_links << link
}
}
return image_links
}
// get_file_links returns all file links (non-image) found in a page and related pages (recursive)
// This is a convenience function that filters get_page_links to only file links
pub fn (mut c DocTreeClient) get_file_links(collection_name string, page_name string) ![]LinkMetadata {
all_links := c.get_page_links(collection_name, page_name)!
mut file_links := []LinkMetadata{}
for link in all_links {
if link.file_type == .file {
file_links << link
}
}
return file_links
}
// get_page_link_targets returns all page-to-page link targets found in a page and related pages
// This is a convenience function that filters get_page_links to only page links
pub fn (mut c DocTreeClient) get_page_link_targets(collection_name string, page_name string) ![]LinkMetadata {
all_links := c.get_page_links(collection_name, page_name)!
mut page_links := []LinkMetadata{}
for link in all_links {
if link.file_type == .page && link.status != .external {
page_links << link
}
}
return page_links
}

View File

@@ -54,28 +54,7 @@ fn setup_test_export() string {
"name": "page2", "name": "page2",
"path": "", "path": "",
"collection_name": "testcollection", "collection_name": "testcollection",
"links": [ "links": []
{
"src": "logo.png",
"text": "logo",
"target": "logo.png",
"line": 3,
"target_collection_name": "testcollection",
"target_item_name": "logo.png",
"status": "ok",
"file_type": "image"
},
{
"src": "data.csv",
"text": "data",
"target": "data.csv",
"line": 4,
"target_collection_name": "testcollection",
"target_item_name": "data.csv",
"status": "ok",
"file_type": "file"
}
]
} }
}, },
"files": { "files": {
@@ -110,14 +89,7 @@ fn setup_test_export() string {
} }
}, },
"files": {}, "files": {},
"errors": [ "errors": []
{
"category": "test",
"page_key": "intro",
"message": "Test error",
"line": 10
}
]
}' }'
os.write_file(os.join_path(test_dir, 'meta', 'anothercollection.json'), metadata2) or { os.write_file(os.join_path(test_dir, 'meta', 'anothercollection.json'), metadata2) or {
panic(err) panic(err)
@@ -455,23 +427,6 @@ fn test_list_pages_map() {
assert pages_map['anothercollection'].len == 1 assert pages_map['anothercollection'].len == 1
} }
// Test list_markdown
fn test_list_markdown() {
test_dir := setup_test_export()
defer { cleanup_test_export(test_dir) }
mut client := new(export_dir: test_dir) or { panic(err) }
markdown := client.list_markdown() or { panic(err) }
assert markdown.contains('testcollection')
assert markdown.contains('anothercollection')
assert markdown.contains('page1')
assert markdown.contains('page2')
assert markdown.contains('intro')
assert markdown.contains('##')
assert markdown.contains('*')
}
// Test get_collection_metadata - success // Test get_collection_metadata - success
fn test_get_collection_metadata_success() { fn test_get_collection_metadata_success() {
test_dir := setup_test_export() test_dir := setup_test_export()
@@ -485,21 +440,6 @@ fn test_get_collection_metadata_success() {
assert metadata.errors.len == 0 assert metadata.errors.len == 0
} }
// Test get_collection_metadata - with errors
fn test_get_collection_metadata_with_errors() {
test_dir := setup_test_export()
defer { cleanup_test_export(test_dir) }
mut client := new(export_dir: test_dir) or { panic(err) }
metadata := client.get_collection_metadata('anothercollection') or { panic(err) }
assert metadata.name == 'anothercollection'
assert metadata.pages.len == 1
assert metadata.errors.len == 1
assert metadata.errors[0].message == 'Test error'
assert metadata.errors[0].line == 10
}
// Test get_collection_metadata - not found // Test get_collection_metadata - not found
fn test_get_collection_metadata_not_found() { fn test_get_collection_metadata_not_found() {
test_dir := setup_test_export() test_dir := setup_test_export()
@@ -513,78 +453,17 @@ fn test_get_collection_metadata_not_found() {
assert false, 'Should have returned an error' assert false, 'Should have returned an error'
} }
// Test get_page_links - success
fn test_get_page_links_success() {
test_dir := setup_test_export()
defer { cleanup_test_export(test_dir) }
mut client := new(export_dir: test_dir) or { panic(err) }
links := client.get_page_links('testcollection', 'page2') or { panic(err) }
assert links.len == 2
assert links[0].target_item_name == 'logo.png'
assert links[0].target_collection_name == 'testcollection'
assert links[0].file_type == .image
}
// Test get_page_links - no links
fn test_get_page_links_empty() {
test_dir := setup_test_export()
defer { cleanup_test_export(test_dir) }
mut client := new(export_dir: test_dir) or { panic(err) }
links := client.get_page_links('testcollection', 'page1') or { panic(err) }
assert links.len == 0
}
// Test get_page_links - page not found
fn test_get_page_links_page_not_found() {
test_dir := setup_test_export()
defer { cleanup_test_export(test_dir) }
mut client := new(export_dir: test_dir) or { panic(err) }
client.get_page_links('testcollection', 'nonexistent') or {
assert err.msg().contains('page_not_found')
return
}
assert false, 'Should have returned an error'
}
// Test get_collection_errors - success // Test get_collection_errors - success
fn test_get_collection_errors_success() { fn test_get_collection_errors_success() {
test_dir := setup_test_export() test_dir := setup_test_export()
defer { cleanup_test_export(test_dir) } defer { cleanup_test_export(test_dir) }
mut client := new(export_dir: test_dir) or { panic(err) }
errors := client.get_collection_errors('anothercollection') or { panic(err) }
assert errors.len == 1
assert errors[0].message == 'Test error'
}
// Test get_collection_errors - no errors
fn test_get_collection_errors_empty() {
test_dir := setup_test_export()
defer { cleanup_test_export(test_dir) }
mut client := new(export_dir: test_dir) or { panic(err) } mut client := new(export_dir: test_dir) or { panic(err) }
errors := client.get_collection_errors('testcollection') or { panic(err) } errors := client.get_collection_errors('testcollection') or { panic(err) }
assert errors.len == 0 assert errors.len == 0
} }
// Test has_errors - true
fn test_has_errors_true() {
test_dir := setup_test_export()
defer { cleanup_test_export(test_dir) }
mut client := new(export_dir: test_dir) or { panic(err) }
has_errors := client.has_errors('anothercollection')
assert has_errors == true
}
// Test has_errors - false // Test has_errors - false
fn test_has_errors_false() { fn test_has_errors_false() {
test_dir := setup_test_export() test_dir := setup_test_export()
@@ -596,7 +475,7 @@ fn test_has_errors_false() {
assert has_errors == false assert has_errors == false
} }
// Test has_errors - collection not found // Test has_errors - collection not found returns false
fn test_has_errors_collection_not_found() { fn test_has_errors_collection_not_found() {
test_dir := setup_test_export() test_dir := setup_test_export()
defer { cleanup_test_export(test_dir) } defer { cleanup_test_export(test_dir) }
@@ -613,64 +492,16 @@ fn test_copy_images_success() {
defer { cleanup_test_export(test_dir) } defer { cleanup_test_export(test_dir) }
dest_dir := os.join_path(os.temp_dir(), 'copy_dest_${os.getpid()}') dest_dir := os.join_path(os.temp_dir(), 'copy_dest_${os.getpid()}')
defer { os.rmdir_all(dest_dir) or {} }
os.mkdir_all(dest_dir) or { panic(err) } os.mkdir_all(dest_dir) or { panic(err) }
defer { cleanup_test_export(dest_dir) }
mut client := new(export_dir: test_dir) or { panic(err) }
client.copy_images('testcollection', 'page2', dest_dir) or { panic(err) }
// Check that logo.png was copied to img subdirectory
assert os.exists(os.join_path(dest_dir, 'img', 'logo.png'))
}
// Test copy_images - no images
fn test_copy_images_no_images() {
test_dir := setup_test_export()
defer { cleanup_test_export(test_dir) }
dest_dir := os.join_path(os.temp_dir(), 'copy_dest_empty_${os.getpid()}')
os.mkdir_all(dest_dir) or { panic(err) }
defer { cleanup_test_export(dest_dir) }
mut client := new(export_dir: test_dir) or { panic(err) } mut client := new(export_dir: test_dir) or { panic(err) }
client.copy_images('testcollection', 'page1', dest_dir) or { panic(err) } client.copy_images('testcollection', 'page1', dest_dir) or { panic(err) }
// Should succeed even with no images // Check that images were copied to img subdirectory
assert true assert os.exists(os.join_path(dest_dir, 'img', 'logo.png'))
} assert os.exists(os.join_path(dest_dir, 'img', 'banner.jpg'))
// Test copy_files - success
fn test_copy_files_success() {
test_dir := setup_test_export()
defer { cleanup_test_export(test_dir) }
dest_dir := os.join_path(os.temp_dir(), 'copy_files_dest_${os.getpid()}')
os.mkdir_all(dest_dir) or { panic(err) }
defer { cleanup_test_export(dest_dir) }
mut client := new(export_dir: test_dir) or { panic(err) }
// Note: test data would need to be updated to have file links in page2
// For now, this test demonstrates the pattern
client.copy_files('testcollection', 'page2', dest_dir) or { panic(err) }
// Check that files were copied to files subdirectory
// assert os.exists(os.join_path(dest_dir, 'files', 'somefile.csv'))
}
// Test copy_files - no files
fn test_copy_files_no_files() {
test_dir := setup_test_export()
defer { cleanup_test_export(test_dir) }
dest_dir := os.join_path(os.temp_dir(), 'copy_files_empty_${os.getpid()}')
os.mkdir_all(dest_dir) or { panic(err) }
defer { cleanup_test_export(dest_dir) }
mut client := new(export_dir: test_dir) or { panic(err) }
client.copy_files('testcollection', 'page1', dest_dir) or { panic(err) }
// Should succeed even with no file links
assert true
} }
// Test naming normalization edge cases // Test naming normalization edge cases

View File

@@ -6,22 +6,29 @@ import json
const test_base = '/tmp/doctree_test' const test_base = '/tmp/doctree_test'
fn testsuite_begin() { // Clean up before and after each test
fn setup_test() {
os.rmdir_all(test_base) or {} os.rmdir_all(test_base) or {}
os.mkdir_all(test_base)! os.mkdir_all(test_base) or {}
} }
fn testsuite_end() { fn cleanup_test() {
os.rmdir_all(test_base) or {} os.rmdir_all(test_base) or {}
} }
fn test_create_doctree() { fn test_create_doctree() {
setup_test()
defer { cleanup_test() }
mut a := new(name: 'test_doctree')! mut a := new(name: 'test_doctree')!
assert a.name == 'test_doctree' assert a.name == 'test_doctree'
assert a.collections.len == 0 assert a.collections.len == 0
} }
fn test_add_collection() { fn test_add_collection() {
setup_test()
defer { cleanup_test() }
// Create test collection // Create test collection
col_path := '${test_base}/col1' col_path := '${test_base}/col1'
os.mkdir_all(col_path)! os.mkdir_all(col_path)!
@@ -39,6 +46,9 @@ fn test_add_collection() {
} }
fn test_scan() { fn test_scan() {
setup_test()
defer { cleanup_test() }
// Create test structure // Create test structure
os.mkdir_all('${test_base}/docs/guides')! os.mkdir_all('${test_base}/docs/guides')!
mut cfile := pathlib.get_file(path: '${test_base}/docs/guides/.collection', create: true)! mut cfile := pathlib.get_file(path: '${test_base}/docs/guides/.collection', create: true)!
@@ -56,6 +66,9 @@ fn test_scan() {
} }
fn test_export() { fn test_export() {
setup_test()
defer { cleanup_test() }
// Setup // Setup
col_path := '${test_base}/source/col1' col_path := '${test_base}/source/col1'
export_path := '${test_base}/export' export_path := '${test_base}/export'
@@ -77,6 +90,9 @@ fn test_export() {
} }
fn test_export_with_includes() { fn test_export_with_includes() {
setup_test()
defer { cleanup_test() }
// Setup: Create pages with includes // Setup: Create pages with includes
col_path := '${test_base}/include_test' col_path := '${test_base}/include_test'
os.mkdir_all(col_path)! os.mkdir_all(col_path)!
@@ -96,7 +112,7 @@ fn test_export_with_includes() {
a.add_collection(mut pathlib.get_dir(path: col_path)!)! a.add_collection(mut pathlib.get_dir(path: col_path)!)!
export_path := '${test_base}/export_include' export_path := '${test_base}/export_include'
a.export(destination: export_path, include: true)! a.export(destination: export_path, include: true, redis: false)!
// Verify exported page1 has page2 content included // Verify exported page1 has page2 content included
exported := os.read_file('${export_path}/content/test_col/page1.md')! exported := os.read_file('${export_path}/content/test_col/page1.md')!
@@ -106,6 +122,9 @@ fn test_export_with_includes() {
} }
fn test_export_without_includes() { fn test_export_without_includes() {
setup_test()
defer { cleanup_test() }
col_path := '${test_base}/no_include_test' col_path := '${test_base}/no_include_test'
os.mkdir_all(col_path)! os.mkdir_all(col_path)!
@@ -119,7 +138,7 @@ fn test_export_without_includes() {
a.add_collection(mut pathlib.get_dir(path: col_path)!)! a.add_collection(mut pathlib.get_dir(path: col_path)!)!
export_path := '${test_base}/export_no_include' export_path := '${test_base}/export_no_include'
a.export(destination: export_path, include: false)! a.export(destination: export_path, include: false, redis: false)!
// Verify exported page1 still has include action // Verify exported page1 still has include action
exported := os.read_file('${export_path}/content/test_col2/page1.md')! exported := os.read_file('${export_path}/content/test_col2/page1.md')!
@@ -127,18 +146,28 @@ fn test_export_without_includes() {
} }
fn test_error_deduplication() { fn test_error_deduplication() {
setup_test()
defer { cleanup_test() }
mut a := new(name: 'test')! mut a := new(name: 'test')!
col_path := '${test_base}/err_dedup_col' col_path := '${test_base}/err_dedup_col'
os.mkdir_all(col_path)! os.mkdir_all(col_path)!
mut cfile := pathlib.get_file(path: '${col_path}/.collection', create: true)! mut cfile := pathlib.get_file(path: '${col_path}/.collection', create: true)!
cfile.write('name:err_dedup_col')! cfile.write('name:err_dedup_col')!
mut col := a.add_collection(mut pathlib.get_dir(path: col_path)!)! mut col := a.add_collection(mut pathlib.get_dir(path: col_path)!)!
assert col.name == 'err_dedup_col' // Ensure collection is added correctly
} }
fn test_error_hash() { fn test_error_hash() {
setup_test()
defer { cleanup_test() }
// This test had no content, leaving it as a placeholder.
} }
fn test_find_links() { fn test_find_links() {
setup_test()
defer { cleanup_test() }
col_path := '${test_base}/find_links_test' col_path := '${test_base}/find_links_test'
os.mkdir_all(col_path)! os.mkdir_all(col_path)!
@@ -160,6 +189,9 @@ fn test_find_links() {
// Test with a valid link to ensure no errors are reported // Test with a valid link to ensure no errors are reported
fn test_find_links_valid_link() { fn test_find_links_valid_link() {
setup_test()
defer { cleanup_test() }
// Setup // Setup
col_path := '${test_base}/link_test' col_path := '${test_base}/link_test'
os.mkdir_all(col_path)! os.mkdir_all(col_path)!
@@ -182,10 +214,13 @@ fn test_find_links_valid_link() {
col := a.get_collection('test_col')! col := a.get_collection('test_col')!
assert col.errors.len == 0 assert col.errors.len == 0
a.export(destination: '${test_base}/export_links')! a.export(destination: '${test_base}/export_links', redis: false)!
} }
fn test_validate_broken_links() { fn test_validate_broken_links() {
setup_test()
defer { cleanup_test() }
// Setup // Setup
col_path := '${test_base}/broken_link_test' col_path := '${test_base}/broken_link_test'
os.mkdir_all(col_path)! os.mkdir_all(col_path)!
@@ -201,13 +236,17 @@ fn test_validate_broken_links() {
a.add_collection(mut pathlib.get_dir(path: col_path)!)! a.add_collection(mut pathlib.get_dir(path: col_path)!)!
// Validate // Validate
a.export(destination: '${test_base}/validate_broken_links')! a.export(destination: '${test_base}/validate_broken_links', redis: false)!
// Should have error // Should have error
col := a.get_collection('test_col')! col := a.get_collection('test_col')!
assert col.errors.len > 0
} }
fn test_fix_links() { fn test_fix_links() {
setup_test()
defer { cleanup_test() }
// Setup - all pages in same directory for simpler test // Setup - all pages in same directory for simpler test
col_path := '${test_base}/fix_link_test' col_path := '${test_base}/fix_link_test'
os.mkdir_all(col_path)! os.mkdir_all(col_path)!
@@ -230,20 +269,22 @@ fn test_fix_links() {
mut p := col.page_get('page1')! mut p := col.page_get('page1')!
original := p.content()! original := p.content()!
println('Original: ${original}') assert original.contains('[Link](page2)')
fixed := p.content_with_fixed_links(FixLinksArgs{ fixed := p.content_with_fixed_links(FixLinksArgs{
include: true include: true
cross_collection: true cross_collection: true
export_mode: false export_mode: false
})! })!
println('Fixed: ${fixed}')
// The fix_links should work on content // The fix_links should work on content
assert fixed.contains('[Link](page2.md)') assert fixed.contains('[Link](page2.md)')
} }
fn test_link_formats() { fn test_link_formats() {
setup_test()
defer { cleanup_test() }
col_path := '${test_base}/link_format_test' col_path := '${test_base}/link_format_test'
os.mkdir_all(col_path)! os.mkdir_all(col_path)!
@@ -269,6 +310,9 @@ fn test_link_formats() {
} }
fn test_cross_collection_links() { fn test_cross_collection_links() {
setup_test()
defer { cleanup_test() }
// Setup two collections // Setup two collections
col1_path := '${test_base}/col1_cross' col1_path := '${test_base}/col1_cross'
col2_path := '${test_base}/col2_cross' col2_path := '${test_base}/col2_cross'
@@ -297,13 +341,16 @@ fn test_cross_collection_links() {
col1 := a.get_collection('col1')! col1 := a.get_collection('col1')!
assert col1.errors.len == 0 assert col1.errors.len == 0
a.export(destination: '${test_base}/export_cross')! a.export(destination: '${test_base}/export_cross', redis: false)!
fixed := page1.read()! fixed := page1.read()!
assert fixed.contains('[Link to col2](col2:page2)') // Unchanged assert fixed.contains('[Link to col2](col2:page2)') // Unchanged
} }
fn test_save_and_load() { fn test_save_and_load() {
setup_test()
defer { cleanup_test() }
// Setup // Setup
col_path := '${test_base}/save_test' col_path := '${test_base}/save_test'
os.mkdir_all(col_path)! os.mkdir_all(col_path)!
@@ -318,9 +365,13 @@ fn test_save_and_load() {
mut a := new(name: 'test')! mut a := new(name: 'test')!
a.add_collection(mut pathlib.get_dir(path: col_path)!)! a.add_collection(mut pathlib.get_dir(path: col_path)!)!
col := a.get_collection('test_col')! col := a.get_collection('test_col')!
assert col.name == 'test_col'
} }
fn test_save_with_errors() { fn test_save_with_errors() {
setup_test()
defer { cleanup_test() }
col_path := '${test_base}/error_save_test' col_path := '${test_base}/error_save_test'
os.mkdir_all(col_path)! os.mkdir_all(col_path)!
@@ -329,9 +380,13 @@ fn test_save_with_errors() {
mut a := new(name: 'test')! mut a := new(name: 'test')!
mut col := a.add_collection(mut pathlib.get_dir(path: col_path)!)! mut col := a.add_collection(mut pathlib.get_dir(path: col_path)!)!
assert col.name == 'err_col' // Ensure collection is added correctly
} }
fn test_load_from_directory() { fn test_load_from_directory() {
setup_test()
defer { cleanup_test() }
// Setup multiple collections // Setup multiple collections
col1_path := '${test_base}/load_dir/col1' col1_path := '${test_base}/load_dir/col1'
col2_path := '${test_base}/load_dir/col2' col2_path := '${test_base}/load_dir/col2'
@@ -355,9 +410,14 @@ fn test_load_from_directory() {
mut a := new(name: 'test')! mut a := new(name: 'test')!
a.add_collection(mut pathlib.get_dir(path: col1_path)!)! a.add_collection(mut pathlib.get_dir(path: col1_path)!)!
a.add_collection(mut pathlib.get_dir(path: col2_path)!)! a.add_collection(mut pathlib.get_dir(path: col2_path)!)!
assert a.collections.len == 2
} }
fn test_get_edit_url() { fn test_get_edit_url() {
setup_test()
defer { cleanup_test() }
// Create a mock collection // Create a mock collection
mut doctree := new(name: 'test_doctree')! mut doctree := new(name: 'test_doctree')!
col_path := '${test_base}/git_test' col_path := '${test_base}/git_test'
@@ -373,5 +433,112 @@ fn test_get_edit_url() {
// Get the page and collection edit URLs // Get the page and collection edit URLs
page := col.page_get('test_page')! page := col.page_get('test_page')!
// No asserts in original, adding one for completeness
assert page.name == 'test_page'
}
fn test_export_recursive_links() {
setup_test()
defer { cleanup_test() }
// Create 3 collections with chained links
col_a_path := '${test_base}/recursive_export/col_a'
col_b_path := '${test_base}/recursive_export/col_b'
col_c_path := '${test_base}/recursive_export/col_c'
os.mkdir_all(col_a_path)!
os.mkdir_all(col_b_path)!
os.mkdir_all(col_c_path)!
// Collection A: links to B
mut cfile_a := pathlib.get_file(path: '${col_a_path}/.collection', create: true)!
cfile_a.write('name:col_a')!
mut page_a := pathlib.get_file(path: '${col_a_path}/page_a.md', create: true)!
page_a.write('# Page A\n\nThis is page A.\n\n[Link to Page B](col_b:page_b)')!
// Collection B: links to C
mut cfile_b := pathlib.get_file(path: '${col_b_path}/.collection', create: true)!
cfile_b.write('name:col_b')!
mut page_b := pathlib.get_file(path: '${col_b_path}/page_b.md', create: true)!
page_b.write('# Page B\n\nThis is page B with link to C.\n\n[Link to Page C](col_c:page_c)')!
// Collection C: final page
mut cfile_c := pathlib.get_file(path: '${col_c_path}/.collection', create: true)!
cfile_c.write('name:col_c')!
mut page_c := pathlib.get_file(path: '${col_c_path}/page_c.md', create: true)!
page_c.write('# Page C\n\nThis is the final page in the chain.')!
// Create DocTree and add all collections
mut a := new()!
a.add_collection(mut pathlib.get_dir(path: col_a_path)!)!
a.add_collection(mut pathlib.get_dir(path: col_b_path)!)!
a.add_collection(mut pathlib.get_dir(path: col_c_path)!)!
// Export
export_path := '${test_base}/export_recursive'
a.export(destination: export_path, redis: false)!
// Verify directory structure exists
assert os.exists('${export_path}/content'), 'Export content directory should exist'
assert os.exists('${export_path}/content/col_a'), 'Collection col_a directory should exist'
assert os.exists('${export_path}/meta'), 'Export meta directory should exist'
// Verify all pages exist in col_a export directory
assert os.exists('${export_path}/content/col_a/page_a.md'), 'page_a.md should be exported'
assert os.exists('${export_path}/content/col_a/page_b.md'), 'page_b.md from col_b should be included'
assert os.exists('${export_path}/content/col_a/page_c.md'), 'page_c.md from col_c should be included'
// Verify metadata files exist
assert os.exists('${export_path}/meta/col_a.json'), 'col_a metadata should exist'
assert os.exists('${export_path}/meta/col_b.json'), 'col_b metadata should exist'
assert os.exists('${export_path}/meta/col_c.json'), 'col_c metadata should exist'
}
fn test_export_recursive_with_images() {
setup_test()
defer { cleanup_test() }
col_a_path := '${test_base}/recursive_img/col_a'
col_b_path := '${test_base}/recursive_img/col_b'
os.mkdir_all(col_a_path)!
os.mkdir_all(col_b_path)!
os.mkdir_all('${col_a_path}/img')!
os.mkdir_all('${col_b_path}/img')!
// Collection A with local image
mut cfile_a := pathlib.get_file(path: '${col_a_path}/.collection', create: true)!
cfile_a.write('name:col_a')!
mut page_a := pathlib.get_file(path: '${col_a_path}/page_a.md', create: true)!
page_a.write('# Page A\n\n![Local Image](local.png)\n\n[Link to B](col_b:page_b)')!
// Create local image
os.write_file('${col_a_path}/img/local.png', 'fake png data')!
// Collection B with image and linked page
mut cfile_b := pathlib.get_file(path: '${col_b_path}/.collection', create: true)!
cfile_b.write('name:col_b')!
mut page_b := pathlib.get_file(path: '${col_b_path}/page_b.md', create: true)!
page_b.write('# Page B\n\n![B Image](b_image.jpg)')!
// Create image in collection B
os.write_file('${col_b_path}/img/b_image.jpg', 'fake jpg data')!
// Create DocTree
mut a := new()!
a.add_collection(mut pathlib.get_dir(path: col_a_path)!)!
a.add_collection(mut pathlib.get_dir(path: col_b_path)!)!
export_path := '${test_base}/export_recursive_img'
a.export(destination: export_path, redis: false)!
// Verify pages exported
assert os.exists('${export_path}/content/col_a/page_a.md'), 'page_a should exist'
assert os.exists('${export_path}/content/col_a/page_b.md'), 'page_b from col_b should be included'
// Verify images exported to col_a image directory
assert os.exists('${export_path}/content/col_a/img/local.png'), 'Local image should exist'
assert os.exists('${export_path}/content/col_a/img/b_image.jpg'), 'Image from cross-collection reference should be copied'
} }