sync/mod.rs listed 5 of its 9 modules; migrate's doc sat above the v9 SQL; client.rs had items after its test module; the ffi's sync_now doc had fused into client_update's; complete_reminder (ffi and EditorAction) still said recurrence advancement was to come, though the core does it; NoteQuery.view listed views the core never matched and claimed it validated. Test scratch dirs drop the old ts-/iw- prefixes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -223,7 +223,7 @@ pub(crate) fn scratch(tag: &str) -> BlobStore {
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
static SEQ: AtomicU32 = AtomicU32::new(0);
|
||||
let n = SEQ.fetch_add(1, Ordering::Relaxed);
|
||||
let dir = std::env::temp_dir().join(format!("ts-blobs-{}-{n}-{tag}", std::process::id()));
|
||||
let dir = std::env::temp_dir().join(format!("inkwell-blobs-{}-{n}-{tag}", std::process::id()));
|
||||
let _ = fs::remove_dir_all(&dir);
|
||||
BlobStore::new(dir).expect("scratch blob store")
|
||||
}
|
||||
@@ -326,7 +326,7 @@ mod tests {
|
||||
#[test]
|
||||
fn serving_refuses_a_path_that_isnt_a_hash() {
|
||||
// Delegated to `path`, so the traversal guard is the same one `store` uses.
|
||||
publish_root(std::env::temp_dir().join("ts-blobs-serve-guard"));
|
||||
publish_root(std::env::temp_dir().join("inkwell-blobs-serve-guard"));
|
||||
let (status, _, body) = serve("/../../etc/passwd", None);
|
||||
assert_eq!(status, 404);
|
||||
assert!(body.is_empty());
|
||||
|
||||
+69
-69
@@ -594,75 +594,6 @@ fn describe_transport_error(base_url: &str, err: &reqwest::Error) -> String {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn a_share_names_a_person_or_a_group() {
|
||||
assert_eq!(
|
||||
ShareTarget::Member("u1".into()).body("view"),
|
||||
serde_json::json!({ "user_id": "u1", "permission": "view" })
|
||||
);
|
||||
assert_eq!(
|
||||
ShareTarget::Group("g1".into()).body("edit"),
|
||||
serde_json::json!({ "group_id": "g1", "permission": "edit" })
|
||||
);
|
||||
let raw = r#"{"id":"s2","member":null,
|
||||
"group":{"id":"g1","name":"Family","member_count":3},
|
||||
"permission":"edit"}"#;
|
||||
let share: NoteShare = serde_json::from_str(raw).expect("a group share reads");
|
||||
assert!(share.member.is_none());
|
||||
assert_eq!(share.group.expect("group").member_count, 3);
|
||||
// A server from before groups sends no `groups` at all.
|
||||
let older: Directory = serde_json::from_str(r#"{"members":[]}"#).expect("directory");
|
||||
assert!(older.groups.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn urls_join_without_doubling_slashes() {
|
||||
// normalize_base_url has already stripped any trailing slash, so plain
|
||||
// concatenation is correct — this pins that assumption.
|
||||
assert_eq!(
|
||||
config_url("https://notes.example.com"),
|
||||
"https://notes.example.com/api/config"
|
||||
);
|
||||
assert_eq!(
|
||||
device_login_url("https://notes.example.com"),
|
||||
"https://notes.example.com/api/auth/device-login"
|
||||
);
|
||||
assert_eq!(
|
||||
me_url("https://notes.example.com"),
|
||||
"https://notes.example.com/api/auth/me"
|
||||
);
|
||||
assert_eq!(
|
||||
revoke_self_url("https://notes.example.com"),
|
||||
"https://notes.example.com/api/auth/devices/self"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn revoke_outcome_serializes_tagged_for_the_frontend() {
|
||||
// The UI decides between "signed out on the server" and "still valid, go
|
||||
// revoke it" by reading this tag, so its shape is part of the contract.
|
||||
let json = serde_json::to_string(&RevokeOutcome::Failed {
|
||||
reason: "offline".into(),
|
||||
})
|
||||
.expect("outcome serializes");
|
||||
assert!(json.contains("\"status\":\"failed\""), "got {json}");
|
||||
let json = serde_json::to_string(&RevokeOutcome::Revoked).expect("outcome serializes");
|
||||
assert!(json.contains("\"status\":\"revoked\""), "got {json}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn urls_preserve_a_port_and_subpath() {
|
||||
assert_eq!(
|
||||
config_url("http://192.168.1.10:8000/inkwell"),
|
||||
"http://192.168.1.10:8000/inkwell/api/config"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The Android client a linked server can hand out.
|
||||
///
|
||||
/// Mirrors `/api/client/android` (see the server's `client_dist.py`). Absent there
|
||||
@@ -782,3 +713,72 @@ pub async fn download_client(
|
||||
std::fs::rename(&partial, dest)
|
||||
.map_err(|e| format!("Couldn't put the downloaded update in place: {e}"))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn a_share_names_a_person_or_a_group() {
|
||||
assert_eq!(
|
||||
ShareTarget::Member("u1".into()).body("view"),
|
||||
serde_json::json!({ "user_id": "u1", "permission": "view" })
|
||||
);
|
||||
assert_eq!(
|
||||
ShareTarget::Group("g1".into()).body("edit"),
|
||||
serde_json::json!({ "group_id": "g1", "permission": "edit" })
|
||||
);
|
||||
let raw = r#"{"id":"s2","member":null,
|
||||
"group":{"id":"g1","name":"Family","member_count":3},
|
||||
"permission":"edit"}"#;
|
||||
let share: NoteShare = serde_json::from_str(raw).expect("a group share reads");
|
||||
assert!(share.member.is_none());
|
||||
assert_eq!(share.group.expect("group").member_count, 3);
|
||||
// A server from before groups sends no `groups` at all.
|
||||
let older: Directory = serde_json::from_str(r#"{"members":[]}"#).expect("directory");
|
||||
assert!(older.groups.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn urls_join_without_doubling_slashes() {
|
||||
// normalize_base_url has already stripped any trailing slash, so plain
|
||||
// concatenation is correct — this pins that assumption.
|
||||
assert_eq!(
|
||||
config_url("https://notes.example.com"),
|
||||
"https://notes.example.com/api/config"
|
||||
);
|
||||
assert_eq!(
|
||||
device_login_url("https://notes.example.com"),
|
||||
"https://notes.example.com/api/auth/device-login"
|
||||
);
|
||||
assert_eq!(
|
||||
me_url("https://notes.example.com"),
|
||||
"https://notes.example.com/api/auth/me"
|
||||
);
|
||||
assert_eq!(
|
||||
revoke_self_url("https://notes.example.com"),
|
||||
"https://notes.example.com/api/auth/devices/self"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn revoke_outcome_serializes_tagged_for_the_frontend() {
|
||||
// The UI decides between "signed out on the server" and "still valid, go
|
||||
// revoke it" by reading this tag, so its shape is part of the contract.
|
||||
let json = serde_json::to_string(&RevokeOutcome::Failed {
|
||||
reason: "offline".into(),
|
||||
})
|
||||
.expect("outcome serializes");
|
||||
assert!(json.contains("\"status\":\"failed\""), "got {json}");
|
||||
let json = serde_json::to_string(&RevokeOutcome::Revoked).expect("outcome serializes");
|
||||
assert!(json.contains("\"status\":\"revoked\""), "got {json}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn urls_preserve_a_port_and_subpath() {
|
||||
assert_eq!(
|
||||
config_url("http://192.168.1.10:8000/inkwell"),
|
||||
"http://192.168.1.10:8000/inkwell/api/config"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
//! - `client` — HTTP transport: the handshake call and device-token auth.
|
||||
//! - `state` — the persisted link record (server, token, change-feed cursor).
|
||||
//! - `engine` — one full cycle: push local changes, then pull the server's.
|
||||
//! - `link` — linking a device to a server and unlinking it, one flow for every client.
|
||||
//! - `pull` / `push` — the two halves of that cycle.
|
||||
//! - `wire` — the delta-feed JSON shapes, as the server sends them.
|
||||
//! - `blobs` — attachment bytes on disk, filed under their own sha256.
|
||||
//! - `sharing` — the Share dialog's calls, straight to the server (#5175).
|
||||
//!
|
||||
//! The UI surface that drives this lives in whichever client is wrapping the crate,
|
||||
|
||||
Reference in New Issue
Block a user