Expand test coverage for site_generator, importer, downloader, instance (#60) - #73
Merged
Merged
Conversation
Add a #[cfg(test)] db::open_in_memory() that builds an isolated, fully-migrated in-memory database (rarray vtab + foreign_keys, no WAL pragma since it does not apply to :memory:). Reads no config, so DB-dependent unit tests stay hermetic and parallel-safe. Add tempfile as a dev-dependency for the filesystem tests in later phases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…h tests Downloader::run previously discarded each worker's JoinHandle result, so a failed download was silently swallowed. Count per-task failures (logging each), keep draining the queue, and bail after the loop when any failed — the happy path still returns Ok. Add tests for DownloadTask::execute error paths (non-2xx, unwritable destination) and for run() surfacing a failure. Scope: only the failure-surfacing slice of #51; timeouts, a shared client, and streaming-to-disk remain out of scope. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a path-traversal guard in Importer::handle_media_attachment: reject any entry with a ParentDir/RootDir/Prefix component and bail the whole import. The zip path was already safe via enclosed_name(); this closes the tar path, which joined entry paths directly. Tests (src/mastodon/importer.rs): a hand-crafted raw tar with a `../`-escaping entry is rejected end-to-end through import() (regression for #49); unsupported extensions error; the tar.gz and zip export fixtures ingest their outbox/actor via the by-extension dispatch; skip_media extracts no files. Also add db::open_migrated_file(&Path) test helper (file-backed sibling of open_in_memory) so a test can query a DB after handing its Connection to the Importer, which takes ownership. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unit tests for the site_generator paths that already take explicit params, using the in-memory DB helper and the activity fixture: plan_activities_pages day-context building and prev/next linking across two days, generate_index_json write + round-trip, and setup_build_path create/clean (incl. NotFound tolerance on a nonexistent path). No production changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#55) Thread &AppConfig through the functions that read the process-global config singleton, so library code no longer touches it: db::conn/upgrade, templates::init, site_generator::{setup_data_path, unpack_customizable_resources, copy_web_assets, generate_activities_pages, generate_activity_page}, and mastodon::instance::{load,save}_instance_config. The global + config::init() stay as the CLI-boundary source; each command fetches config::config() once and passes &config down. &AppConfig by reference (it's Sync, so the rayon workers in generate_activities_pages borrow it directly — no Arc). Behavior-preserving: verified by the full test suite, tests/exit_code.rs, and a real init/import/build smoke run. config::config() now appears only under src/cli/. build_instance_config_path lost its only fallible step and now returns PathBuf. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
site_generator: render the site index HTML and a per-day activity page via templates::init(&config) + the embedded default theme (the per-day test seeds an on-disk DB since generate_activity_page opens its own connection); copy_web_assets falls back to embedded web assets when no theme dir exists; setup_data_path create/clean. mastodon::instance: save/load round-trip preserves every field, and loading a missing instance returns a fresh default. Suite is now 41 lib tests (from 22 at baseline), covering site_generator, importer, downloader, and instance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record spec/plan/research/notes for the session. Per the final whole-branch review, strengthen generate_activity_page_renders_day to assert the rendered page is a full HTML document containing the day date, rather than merely non-empty. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Expands automated test coverage across fossilizer’s largest side-effecting subsystems (site generation, Mastodon import, media downloading, and instance config persistence) and makes minimal production refactors to make those paths testable and safer (notably config injection, tar-path traversal protection, and surfacing downloader task failures).
Changes:
- Thread
&AppConfigthrough previously-global-config-dependent library code (db/templates/site_generator/instance) and update CLI call sites to fetch config once and pass it down. - Add a test-only SQLite helper (
db::open_in_memory/db::open_migrated_file) and a large set of new unit tests covering site planning/rendering, importer behavior (incl. zip-slip regression), downloader failure surfacing, and instance config round-trips. - Improve downloader behavior to observe worker results and return an error after draining if any tasks failed; add
tempfilefor filesystem-based tests.
Reviewed changes
Copilot reviewed 18 out of 19 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/templates.rs |
Makes template initialization configurable by injecting &AppConfig (removes dependency on global config). |
src/site_generator.rs |
Injects &AppConfig for config-dependent operations and adds extensive unit tests for planning, rendering, assets, and data path setup. |
src/mastodon/instance.rs |
Injects &AppConfig for instance config persistence and adds round-trip tests. |
src/mastodon/importer.rs |
Adds a tar-path zip-slip/path traversal guard and comprehensive importer tests (fixtures + regression archive). |
src/downloader.rs |
Surfaces worker/task failures from Downloader::run and adds tests for error paths and run-loop failure propagation. |
src/db.rs |
Refactors conn/upgrade to accept &AppConfig and adds #[cfg(test)] DB helpers for hermetic, migrated SQLite connections plus tests. |
src/cli/upgrade.rs |
Updates CLI wiring to pass injected config into db::upgrade. |
src/cli/mastodon/fetch.rs |
Updates DB connection creation to use injected config. |
src/cli/mastodon.rs |
Fetches config once at the command boundary and passes it into instance config load/save. |
src/cli/init.rs |
Updates init flow to use injected config for data setup, DB upgrade, and optional customization. |
src/cli/import.rs |
Updates importer CLI to open DB via injected config. |
src/cli/fetch.rs |
Updates DB connection creation to use injected config. |
src/cli/build.rs |
Updates build flow to inject config into asset copy, templates init, DB connection, and per-day generation. |
Cargo.toml |
Adds tempfile dev-dependency to support filesystem-based tests. |
Cargo.lock |
Locks new dev-dependency (tempfile and transitive deps). |
docs/dev-sessions/2026-07-24-1521-expand-test-coverage/spec.md |
Adds spec documenting goals, decisions, and constraints for the test/DI work. |
docs/dev-sessions/2026-07-24-1521-expand-test-coverage/research.md |
Adds research notes mapping config/global usage and test seams. |
docs/dev-sessions/2026-07-24-1521-expand-test-coverage/plan.md |
Adds phased implementation plan and verification checklist. |
docs/dev-sessions/2026-07-24-1521-expand-test-coverage/notes.md |
Adds execution notes, deviations, and verification outcomes for the work. |
Comments suppressed due to low confidence (1)
src/site_generator.rs:145
- generate_activity_page returns Result<()>, but later in the body it unwrap()s the activity’s actor id and the actor lookup, which can panic during a build. Prefer returning an error so failures are surfaced cleanly to callers.
) -> Result<()> {
let db_conn = db::conn(config)?;
let db_activities = db::activities::Activities::new(&db_conn);
let day = &day_entry.current.date;
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
121
to
132
| pub fn generate_activities_pages( | ||
| config: &AppConfig, | ||
| build_path: &PathBuf, | ||
| tera: &Tera, | ||
| actors: &HashMap<String, activitystreams::Actor>, | ||
| day_entries: &Vec<contexts::IndexDayContext>, | ||
| ) -> Result<()> { | ||
| info!("Generating {} per-day pages", day_entries.len()); | ||
| day_entries | ||
| .par_iter() | ||
| .for_each(|day_entry| generate_activity_page(build_path, tera, actors, day_entry).unwrap()); | ||
| day_entries.par_iter().for_each(|day_entry| { | ||
| generate_activity_page(config, build_path, tera, actors, day_entry).unwrap(); | ||
| }); | ||
| Ok(()) |
Comment on lines
+34
to
+38
| fn build_instance_config_path(config: &AppConfig, instance: &String) -> PathBuf { | ||
| // todo: hash the instance domain string rather than using it verbatim? | ||
| Ok(data_path.join(format!("config-instance-{instance}.toml"))) | ||
| config | ||
| .data_path | ||
| .join(format!("config-instance-{instance}.toml")) |
build_instance_config_path embeds the instance name verbatim in the config filename; reject names containing path separators or `..` so a crafted --instance can't escape data_path (same bug class as the tar zip-slip fixed in this PR). Real Mastodon hostnames never contain these characters, so valid names are unaffected. Add a regression test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Owner
Author
Copilot review triageFixed in this PR:
Deferred (pre-existing, out of this PR's scope) → filed as #74:
|
lmorchard
added a commit
that referenced
this pull request
Jul 25, 2026
templates_source built its match/strip prefix with PathBuf (\-delimited on Windows) while rust-embed uses /-delimited paths, so the embedded-template fallback loaded nothing and rendering failed with 'Template index.html not found'. Operate on /-delimited strings; add a platform-independent regression test. Surfaced by the post-merge Windows matrix after #73.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds a real test safety net around fossilizer's largest side-effecting subsystems, plus the minimal production changes that unblock testing them. Suite grows from 22 → 41 lib tests (43 total incl. integration + doc).
What changed
Test coverage (#60) across
site_generator,mastodon/importer,downloader, andmastodon/instance, on top of a new#[cfg(test)]SQLite helper (db::open_in_memory/open_migrated_file).Production changes (minimal, to unblock the above):
&AppConfigthrough the library functions that previously read the process-global config singleton (db::conn/upgrade,templates::init,site_generator::{setup_data_path, unpack_customizable_resources, copy_web_assets, generate_activit*},instance::{load,save}_instance_config). The global +config::init()remain only at the CLI boundary; each command fetchesconfig::config()once and passes&configdown. Behavior-preserving.config::config()now appears only undersrc/cli/.Importer::handle_media_attachmentnow rejects any entry with a../absolute/prefix component and bails the whole import. (The zip path was already safe viaenclosed_name().) Covered by an end-to-end regression test with a hand-crafted../tar entry.Downloader::rundiscarded each worker'sJoinHandleresult, silently swallowing failed downloads. It now counts failures, keeps draining the queue, and returns an error once drained (happy path unchanged). Timeouts / shared client / streaming-to-disk remain out of scope.Design decisions
&AppConfigby reference, notArcor a test-only global setter — it'sSync, so the rayon workers ingenerate_activities_pagesborrow it directly. Simplest change that unblocks isolated, parallel-safe tests. A futureAppContextservices-struct is the scale-up shape (documented in the session notes), deliberately deferred as over-engineering for now.Verification
make check(fmt + clippy-D warnings) andmake testgreen.init → import → buildsmoke run produced validindex.html/index.json+ per-day pages through the refactored config path.Deferred (not in this PR)
Rest of #51 (timeouts, shared client, streaming); #57 (remote-actor URL); the
AppContextservices-struct refactor; the project-wide&String→&strsweep.Spec, plan, research, and notes:
docs/dev-sessions/2026-07-24-1521-expand-test-coverage/.Closes #60
Closes #49
Closes #55
🤖 Generated with Claude Code