tauri-plugin-syncular 0.15.4

A native syncular client inside the Tauri process, exposed to the webview as commands + events (consumes the Rust client core directly — no FFI)
Documentation
//! Mock-runtime tests: build a real `MockRuntime` Tauri app with the plugin
//! registered and a webview window, exercising the plugin's SHELL as far as the
//! mock runtime allows.
//!
//! ## Why this file is thin (honest note)
//!
//! The plugin's command BEHAVIOR is exhaustively covered by the crate's unit
//! tests — `core::tests` (router round-trip, event derivation, file-DB
//! persistence, config validation) and `tests::owner_thread_round_trips_over_
//! mailbox` (the real mailbox host path the Tauri commands forward onto). Those
//! run with zero Tauri machinery.
//!
//! Invoking the plugin's commands THROUGH the mock IPC additionally requires
//! the plugin's ACL manifest to be present in the runtime authority. That
//! manifest is produced by `tauri_build`/`generate_context!` at the CONSUMING
//! app's build time (from the plugin's generated `permissions/`), not by
//! `mock_context(noop_assets())` — a mock context carries no plugin manifests,
//! so a `get_ipc_response` for `plugin:syncular|…` is denied with
//! `UnknownManifest`. Rather than stand up a full `tauri.conf.json` +
//! capabilities + build-codegen fixture just to re-prove the two-line channel
//! forward the commands are, this file verifies what the mock runtime CAN
//! prove: the plugin registers, its `setup` runs (spawning the owning thread
//! and managing the mailbox state), and a window builds against it. The
//! *example app* (bindings/tauri/example) is the end-to-end wiring proof with a
//! real capabilities file.
//!
//! Building a webview needs a windowing backend a headless CI box may lack;
//! each test SKIPS (prints + returns) rather than fails when the window cannot
//! be created, keeping the suite green on a headless runner.

use tauri::test::{mock_builder, mock_context, noop_assets};
use tauri::WebviewWindowBuilder;

fn build_app() -> tauri::App<tauri::test::MockRuntime> {
    let config = tauri_plugin_syncular::SyncularConfig {
        auto_sync: false,
        ..Default::default()
    };
    mock_builder()
        .plugin(tauri_plugin_syncular::init(config))
        .build(mock_context(noop_assets()))
        .expect("failed to build mock app with the syncular plugin")
}

#[test]
fn plugin_registers_and_setup_runs() {
    // Building the app runs the plugin's `setup` hook: it manages the mailbox
    // state and spawns the `syncular-core` owning thread. A panic or error in
    // setup would fail `.build()`; reaching here proves the shell initializes
    // and the plugin is registered under its name.
    let app = build_app();
    // Let the owning thread come up; dropping the app tears it down cleanly
    // (the mailbox Sender drops → the thread's recv disconnects → shutdown).
    std::thread::sleep(std::time::Duration::from_millis(20));
    drop(app);
}

#[test]
fn window_builds_against_the_plugin() {
    let app = build_app();
    // A window built with the plugin registered: the invoke handler is
    // installed. Granting its ACL and invoking commands is the consuming app's
    // concern (proven end-to-end by bindings/tauri/example). On a headless box
    // with no windowing backend this skips rather than fails.
    match WebviewWindowBuilder::new(&app, "main", Default::default()).build() {
        Ok(_window) => {}
        Err(e) => eprintln!("skipping window build: no windowing backend ({e})"),
    }
}