Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions lychee-bin/tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ mod cli {
};
use tempfile::{NamedTempFile, tempdir};
use test_utils::{
fixtures_path, main_command, mock_server, redirecting_mock_server, root_path,
DirBuilder, fixtures_path, main_command, mock_server, redirecting_mock_server, root_path,
};

use uuid::Uuid;
Expand Down Expand Up @@ -2890,37 +2890,45 @@ mod cli {
/// Test the --default-extension option for files without extensions
#[test]
fn test_default_extension_option() -> Result<()> {
let mut file_without_ext = NamedTempFile::new()?;
// Create markdown content but with no file extension
writeln!(file_without_ext, "# Test File")?;
writeln!(file_without_ext, "[Example](https://example.com)")?;
writeln!(file_without_ext, "[Local](local.md)")?;
let temp = tempdir()?;
let dir = DirBuilder::new(temp.path());

let file_without_ext = dir.str(
"file-without-ext",
r#"
# Test File
[Example](https://example.com)
[Local](local.md)
"#,
)?;

// Test with --default-extension md
main_command!()
.arg("--default-extension")
.arg("md")
.arg("--dump")
.arg(file_without_ext.path())
.arg(file_without_ext)
.assert()
.success()
.stdout(contains("https://example.com"));

let mut html_file_without_ext = NamedTempFile::new()?;
// Create HTML content but with no file extension
writeln!(html_file_without_ext, "<html><body>")?;
writeln!(
html_file_without_ext,
"<a href=\"https://html-example.com\">HTML Link</a>"
let html_file_without_ext = dir.str(
"html-without-ext",
r#"
<html><body>
<a href="https://html-example.com">HTML Link</a>
</body></html>
"#,
)?;
writeln!(html_file_without_ext, "</body></html>")?;

// Test with --default-extension html
main_command!()
.arg("--default-extension")
.arg("html")
.arg("--dump")
.arg(html_file_without_ext.path())
.arg(html_file_without_ext)
.assert()
.success()
.stdout(contains("https://html-example.com"));
Expand Down
96 changes: 96 additions & 0 deletions test-utils/src/dir_builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//! Directory builder to generate directories of local files for testing.
//!
//! This module provides [`DirBuilder`] which provides methods to easily
//! populate a given directory with files containing certain links. This
//! is intended to allow test fixtures to be defined within the test code.

use std::fmt::Debug;
use std::fs::OpenOptions;
use std::io::BufWriter;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::result::Result;

pub struct DirBuilder<'a> {
path: &'a Path,
}

impl<'a> DirBuilder<'a> {
pub fn new(path: &'a Path) -> DirBuilder<'a> {
Self { path: path }
}

fn make_path(&self, subpath: &str) -> Result<PathBuf, String> {
let subpath = Path::new(subpath);
if !subpath.is_relative() {
return Err(format!("dir() subpath not relative: {subpath:?}"));
}
Ok(self.path.join(subpath))
}

fn append_bytes(&self, path: &Path, contents: &[u8]) -> Result<(), String> {
println!("{:?}", path);
let file = OpenOptions::new()
.create(true)
.append(true)
.open(path)
.map_err(debug_to_string)?;

let mut file = BufWriter::new(file);
file.write_all(contents).map_err(debug_to_string)?;
file.write_all(b"\n").map_err(debug_to_string)?;

Ok(())
}

pub fn dir(&self, subpath: &str) -> Result<PathBuf, String> {
let path = self.make_path(subpath)?;
std::fs::create_dir_all(&path).map_err(debug_to_string)?;
Ok(path)
}

pub fn raw(&self, subpath: &str, contents: &[u8]) -> Result<PathBuf, String> {
let path = self.make_path(subpath)?;
self.append_bytes(&path, contents)?;
Ok(path)
}

pub fn str(&self, subpath: &str, contents: &str) -> Result<PathBuf, String> {
self.raw(subpath, contents.as_bytes())
}

pub fn html(&self, subpath: &str, links: &[&str]) -> Result<PathBuf, String> {
let mut content = String::new();
for link in links {
content.push_str(&format!("<a href=\"{link}\">link</a>\n"));
}
self.str(subpath, &content)
}

pub fn html_anchors(&self, subpath: &str, ids: &[&str]) -> Result<PathBuf, String> {
let mut content = String::new();
for id in ids {
content.push_str(&format!("<p id=\"{id}\">text</p>"));
}
self.str(subpath, &content)
}

pub fn md(&self, subpath: &str, links: &[&str]) -> Result<PathBuf, String> {
let mut content = String::new();
for link in links {
content.push_str(&format!("[link]({link})\n"));
}
self.str(subpath, &content)
}
}

// https://internals.rust-lang.org/t/to-debug-a-debug-counterpart-of-to-string/11228/3
fn debug_to_string<T: Debug>(t: T) -> String {
use std::fmt::Write;
let mut buf = String::new();
buf.write_fmt(format_args!("{:?}", t))
.expect("a Debug implementation returned an error unexpectedly");
buf.shrink_to_fit();
buf
}
4 changes: 4 additions & 0 deletions test-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
//! This crate does not depend on `lychee-lib` or `lychee-bin`, else we would get dependency cycles.
//! Macros are used instead, so that the importer is responsible for providing the dependencies.

pub mod dir_builder;

pub use dir_builder::DirBuilder;

/// Create a mock web server, which responds with a predefined status when
/// handling a matching request
#[macro_export]
Expand Down
Loading