From 243fb49e9ddf9d6ce112e49bf9ab6bf248f8184e Mon Sep 17 00:00:00 2001 From: Geoffrey Sechter Date: Sun, 22 Feb 2026 17:14:47 -0700 Subject: [PATCH] refactor: split offline test suites into feature-organized submodules Split the two monolithic offline test files into feature-organized submodules for easier navigation and maintenance. CLI offline (64 tests): 13 submodules (usage, issues, teams, projects, documents, cycles, milestones, embeds, comments, auth, self_update, format, labels). SDK offline (39 tests): 2 submodules (queries, mutations). Uses #[path] attributes since Rust integration tests don't follow standard edition 2021 module resolution for subdirectories. --- crates/lineark-sdk/tests/offline.rs | 531 +----------- crates/lineark-sdk/tests/offline/mutations.rs | 150 ++++ crates/lineark-sdk/tests/offline/queries.rs | 378 +++++++++ crates/lineark/tests/offline.rs | 763 +----------------- crates/lineark/tests/offline/auth.rs | 19 + crates/lineark/tests/offline/comments.rs | 20 + crates/lineark/tests/offline/cycles.rs | 59 ++ crates/lineark/tests/offline/documents.rs | 61 ++ crates/lineark/tests/offline/embeds.rs | 30 + crates/lineark/tests/offline/format.rs | 34 + crates/lineark/tests/offline/issues.rs | 133 +++ crates/lineark/tests/offline/labels.rs | 10 + crates/lineark/tests/offline/milestones.rs | 62 ++ crates/lineark/tests/offline/projects.rs | 63 ++ crates/lineark/tests/offline/self_update.rs | 37 + crates/lineark/tests/offline/teams.rs | 10 + crates/lineark/tests/offline/usage.rs | 168 ++++ 17 files changed, 1265 insertions(+), 1263 deletions(-) create mode 100644 crates/lineark-sdk/tests/offline/mutations.rs create mode 100644 crates/lineark-sdk/tests/offline/queries.rs create mode 100644 crates/lineark/tests/offline/auth.rs create mode 100644 crates/lineark/tests/offline/comments.rs create mode 100644 crates/lineark/tests/offline/cycles.rs create mode 100644 crates/lineark/tests/offline/documents.rs create mode 100644 crates/lineark/tests/offline/embeds.rs create mode 100644 crates/lineark/tests/offline/format.rs create mode 100644 crates/lineark/tests/offline/issues.rs create mode 100644 crates/lineark/tests/offline/labels.rs create mode 100644 crates/lineark/tests/offline/milestones.rs create mode 100644 crates/lineark/tests/offline/projects.rs create mode 100644 crates/lineark/tests/offline/self_update.rs create mode 100644 crates/lineark/tests/offline/teams.rs create mode 100644 crates/lineark/tests/offline/usage.rs diff --git a/crates/lineark-sdk/tests/offline.rs b/crates/lineark-sdk/tests/offline.rs index f6048d5..a297380 100644 --- a/crates/lineark-sdk/tests/offline.rs +++ b/crates/lineark-sdk/tests/offline.rs @@ -34,385 +34,6 @@ fn extract_variables(server_requests: &[wiremock::Request]) -> Value { body["variables"].clone() } -// ── TeamsQueryBuilder ─────────────────────────────────────────────────────────────── - -#[tokio::test] -async fn teams_first_sets_variable() { - let (server, client) = setup("teams").await; - let _ = client.teams::().first(42).send().await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["first"], 42); - assert_eq!(vars["last"], Value::Null); -} - -#[tokio::test] -async fn teams_last_sets_variable() { - let (server, client) = setup("teams").await; - let _ = client.teams::().last(7).send().await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["last"], 7); - assert_eq!(vars["first"], Value::Null); -} - -#[tokio::test] -async fn teams_before_after_set_variables() { - let (server, client) = setup("teams").await; - let _ = client - .teams::() - .before("cursor-abc") - .after("cursor-xyz") - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["before"], "cursor-abc"); - assert_eq!(vars["after"], "cursor-xyz"); -} - -#[tokio::test] -async fn teams_include_archived_sets_variable() { - let (server, client) = setup("teams").await; - let _ = client.teams::().include_archived(true).send().await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["includeArchived"], true); -} - -#[tokio::test] -async fn teams_all_params_chain() { - let (server, client) = setup("teams").await; - let _ = client - .teams::() - .first(10) - .after("cur") - .include_archived(false) - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["first"], 10); - assert_eq!(vars["after"], "cur"); - assert_eq!(vars["includeArchived"], false); - assert_eq!(vars["before"], Value::Null); - assert_eq!(vars["last"], Value::Null); -} - -#[tokio::test] -async fn teams_no_params_sends_all_null() { - let (server, client) = setup("teams").await; - let _ = client.teams::().send().await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["first"], Value::Null); - assert_eq!(vars["last"], Value::Null); - assert_eq!(vars["before"], Value::Null); - assert_eq!(vars["after"], Value::Null); - assert_eq!(vars["includeArchived"], Value::Null); -} - -// ── UsersQueryBuilder ─────────────────────────────────────────────────────────────── - -#[tokio::test] -async fn users_include_disabled_sets_variable() { - let (server, client) = setup("users").await; - let _ = client - .users::() - .include_disabled(true) - .last(5) - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["includeDisabled"], true); - assert_eq!(vars["last"], 5); -} - -// ── SearchIssuesQueryBuilder (has required `term` arg) ────────────────────────────── - -#[tokio::test] -async fn search_issues_term_is_required() { - let (server, client) = setup("searchIssues").await; - let _ = client - .search_issues::("my query") - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["term"], "my query"); -} - -#[tokio::test] -async fn search_issues_all_optional_params() { - let (server, client) = setup("searchIssues").await; - let _ = client - .search_issues::("bug") - .first(20) - .include_comments(true) - .team_id("team-uuid-123") - .include_archived(true) - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["term"], "bug"); - assert_eq!(vars["first"], 20); - assert_eq!(vars["includeComments"], true); - assert_eq!(vars["teamId"], "team-uuid-123"); - assert_eq!(vars["includeArchived"], true); - // Unset optionals are null. - assert_eq!(vars["last"], Value::Null); - assert_eq!(vars["before"], Value::Null); - assert_eq!(vars["after"], Value::Null); -} - -#[tokio::test] -async fn search_issues_string_setters_accept_str_ref() { - let (server, client) = setup("searchIssues").await; - // All string setters should accept &str (via impl Into). - let _ = client - .search_issues::("term") - .before("b") - .after("a") - .team_id("t") - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["term"], "term"); - assert_eq!(vars["before"], "b"); - assert_eq!(vars["after"], "a"); - assert_eq!(vars["teamId"], "t"); -} - -// ── IssuesQueryBuilder ────────────────────────────────────────────────────────────── - -#[tokio::test] -async fn issues_first_and_include_archived() { - let (server, client) = setup("issues").await; - let _ = client - .issues::() - .first(100) - .include_archived(true) - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["first"], 100); - assert_eq!(vars["includeArchived"], true); -} - -// ── CyclesQueryBuilder ────────────────────────────────────────────────────────────── - -#[tokio::test] -async fn cycles_first_sets_variable() { - let (server, client) = setup("cycles").await; - let _ = client.cycles::().first(50).send().await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["first"], 50); -} - -// ── IssueLabelsQueryBuilder ───────────────────────────────────────────────────────── - -#[tokio::test] -async fn issue_labels_first_sets_variable() { - let (server, client) = setup("issueLabels").await; - let _ = client.issue_labels::().first(250).send().await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["first"], 250); -} - -// ── ProjectsQueryBuilder ──────────────────────────────────────────────────────────── - -#[tokio::test] -async fn projects_last_and_before() { - let (server, client) = setup("projects").await; - let _ = client - .projects::() - .last(25) - .before("cur-end") - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["last"], 25); - assert_eq!(vars["before"], "cur-end"); -} - -// ── WorkflowStatesQueryBuilder ────────────────────────────────────────────────────── - -#[tokio::test] -async fn workflow_states_first_sets_variable() { - let (server, client) = setup("workflowStates").await; - let _ = client - .workflow_states::() - .first(50) - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["first"], 50); -} - -// ── DocumentsQueryBuilder ────────────────────────────────────────────────────────── - -#[tokio::test] -async fn documents_first_sets_variable() { - let (server, client) = setup("documents").await; - let _ = client.documents::().first(20).send().await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["first"], 20); -} - -#[tokio::test] -async fn documents_last_sets_variable() { - let (server, client) = setup("documents").await; - let _ = client.documents::().last(8).send().await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["last"], 8); - assert_eq!(vars["first"], Value::Null); -} - -#[tokio::test] -async fn documents_after_sets_variable() { - let (server, client) = setup("documents").await; - let _ = client - .documents::() - .first(10) - .after("cursor-abc") - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["after"], "cursor-abc"); -} - -#[tokio::test] -async fn documents_before_sets_variable() { - let (server, client) = setup("documents").await; - let _ = client - .documents::() - .before("cursor-end") - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["before"], "cursor-end"); -} - -#[tokio::test] -async fn documents_include_archived_sets_variable() { - let (server, client) = setup("documents").await; - let _ = client - .documents::() - .include_archived(true) - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["includeArchived"], true); -} - -#[tokio::test] -async fn documents_all_params_chain() { - let (server, client) = setup("documents").await; - let _ = client - .documents::() - .first(15) - .after("cur-start") - .include_archived(true) - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["first"], 15); - assert_eq!(vars["after"], "cur-start"); - assert_eq!(vars["includeArchived"], true); - assert_eq!(vars["before"], Value::Null); - assert_eq!(vars["last"], Value::Null); -} - -#[tokio::test] -async fn documents_no_params_sends_all_null() { - let (server, client) = setup("documents").await; - let _ = client.documents::().send().await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["first"], Value::Null); - assert_eq!(vars["last"], Value::Null); - assert_eq!(vars["before"], Value::Null); - assert_eq!(vars["after"], Value::Null); - assert_eq!(vars["includeArchived"], Value::Null); -} - -// ── IssueRelationsQueryBuilder ───────────────────────────────────────────────────── - -#[tokio::test] -async fn issue_relations_first_sets_variable() { - let (server, client) = setup("issueRelations").await; - let _ = client - .issue_relations::() - .first(25) - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["first"], 25); -} - -#[tokio::test] -async fn issue_relations_last_sets_variable() { - let (server, client) = setup("issueRelations").await; - let _ = client - .issue_relations::() - .last(3) - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["last"], 3); - assert_eq!(vars["first"], Value::Null); -} - -#[tokio::test] -async fn issue_relations_before_after_set_variables() { - let (server, client) = setup("issueRelations").await; - let _ = client - .issue_relations::() - .before("cursor-b") - .after("cursor-a") - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["before"], "cursor-b"); - assert_eq!(vars["after"], "cursor-a"); -} - -#[tokio::test] -async fn issue_relations_include_archived_sets_variable() { - let (server, client) = setup("issueRelations").await; - let _ = client - .issue_relations::() - .include_archived(true) - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["includeArchived"], true); -} - -#[tokio::test] -async fn issue_relations_all_params_chain() { - let (server, client) = setup("issueRelations").await; - let _ = client - .issue_relations::() - .first(30) - .after("rel-cursor") - .include_archived(false) - .send() - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["first"], 30); - assert_eq!(vars["after"], "rel-cursor"); - assert_eq!(vars["includeArchived"], false); - assert_eq!(vars["before"], Value::Null); - assert_eq!(vars["last"], Value::Null); -} - -#[tokio::test] -async fn issue_relations_no_params_sends_all_null() { - let (server, client) = setup("issueRelations").await; - let _ = client.issue_relations::().send().await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["first"], Value::Null); - assert_eq!(vars["last"], Value::Null); - assert_eq!(vars["before"], Value::Null); - assert_eq!(vars["after"], Value::Null); - assert_eq!(vars["includeArchived"], Value::Null); -} - -// ── Mutation variable tests ───────────────────────────────────────────────── - async fn setup_mutation(data_path: &str) -> (MockServer, Client) { let server = MockServer::start().await; Mock::given(method("POST")) @@ -431,151 +52,7 @@ async fn setup_mutation(data_path: &str) -> (MockServer, Client) { (server, client) } -#[tokio::test] -async fn document_create_sends_input_variable() { - use lineark_sdk::generated::inputs::DocumentCreateInput; - - let (server, client) = setup_mutation("documentCreate").await; - let input = DocumentCreateInput { - title: Some("Test Document".to_string()), - content: Some("# Hello".to_string()), - ..Default::default() - }; - let _ = client.document_create::(input).await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["input"]["title"], "Test Document"); - assert_eq!(vars["input"]["content"], "# Hello"); -} - -#[tokio::test] -async fn document_update_sends_input_and_id() { - use lineark_sdk::generated::inputs::DocumentUpdateInput; - - let (server, client) = setup_mutation("documentUpdate").await; - let input = DocumentUpdateInput { - title: Some("Updated Title".to_string()), - ..Default::default() - }; - let _ = client - .document_update::(input, "doc-uuid-123".to_string()) - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["input"]["title"], "Updated Title"); - assert_eq!(vars["id"], "doc-uuid-123"); -} - -#[tokio::test] -async fn document_delete_sends_id() { - let (server, client) = setup_mutation("documentDelete").await; - let _ = client - .document_delete::("doc-uuid-456".to_string()) - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["id"], "doc-uuid-456"); -} - -#[tokio::test] -async fn issue_relation_create_sends_input() { - use lineark_sdk::generated::enums::IssueRelationType; - use lineark_sdk::generated::inputs::IssueRelationCreateInput; - - let (server, client) = setup_mutation("issueRelationCreate").await; - let input = IssueRelationCreateInput { - issue_id: Some("issue-a".to_string()), - related_issue_id: Some("issue-b".to_string()), - r#type: Some(IssueRelationType::Blocks), - ..Default::default() - }; - let _ = client - .issue_relation_create::(None, input) - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["input"]["issueId"], "issue-a"); - assert_eq!(vars["input"]["relatedIssueId"], "issue-b"); - assert_eq!(vars["input"]["type"], "blocks"); - assert_eq!(vars["overrideCreatedAt"], Value::Null); -} - -#[tokio::test] -async fn file_upload_sends_required_params() { - let (server, client) = setup_mutation("fileUpload").await; - let _ = client - .file_upload( - None, - Some(true), - 1024, - "image/png".to_string(), - "screenshot.png".to_string(), - ) - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["size"], 1024); - assert_eq!(vars["contentType"], "image/png"); - assert_eq!(vars["filename"], "screenshot.png"); - assert_eq!(vars["makePublic"], true); - assert_eq!(vars["metaData"], Value::Null); -} - -#[tokio::test] -async fn image_upload_from_url_sends_url() { - let (server, client) = setup_mutation("imageUploadFromUrl").await; - let _ = client - .image_upload_from_url("https://example.com/image.png".to_string()) - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["url"], "https://example.com/image.png"); -} - -#[tokio::test] -async fn issue_archive_sends_id_and_trash() { - let (server, client) = setup_mutation("issueArchive").await; - let _ = client - .issue_archive::(Some(true), "issue-uuid-arch".to_string()) - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["id"], "issue-uuid-arch"); - assert_eq!(vars["trash"], true); -} - -#[tokio::test] -async fn issue_archive_without_trash_sends_null() { - let (server, client) = setup_mutation("issueArchive").await; - let _ = client - .issue_archive::(None, "issue-uuid-arch2".to_string()) - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["id"], "issue-uuid-arch2"); - assert_eq!(vars["trash"], Value::Null); -} - -#[tokio::test] -async fn issue_unarchive_sends_id() { - let (server, client) = setup_mutation("issueUnarchive").await; - let _ = client - .issue_unarchive::("issue-uuid-unarch".to_string()) - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["id"], "issue-uuid-unarch"); -} - -#[tokio::test] -async fn issue_delete_sends_id_and_permanently_delete() { - let (server, client) = setup_mutation("issueDelete").await; - let _ = client - .issue_delete::(Some(true), "issue-uuid-123".to_string()) - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["id"], "issue-uuid-123"); - assert_eq!(vars["permanentlyDelete"], true); -} - -#[tokio::test] -async fn issue_delete_without_permanently_sends_null() { - let (server, client) = setup_mutation("issueDelete").await; - let _ = client - .issue_delete::(None, "issue-uuid-456".to_string()) - .await; - let vars = extract_variables(&server.received_requests().await.unwrap()); - assert_eq!(vars["id"], "issue-uuid-456"); - assert_eq!(vars["permanentlyDelete"], Value::Null); -} +#[path = "offline/mutations.rs"] +mod mutations; +#[path = "offline/queries.rs"] +mod queries; diff --git a/crates/lineark-sdk/tests/offline/mutations.rs b/crates/lineark-sdk/tests/offline/mutations.rs new file mode 100644 index 0000000..8fd99cf --- /dev/null +++ b/crates/lineark-sdk/tests/offline/mutations.rs @@ -0,0 +1,150 @@ +use super::*; + +#[tokio::test] +async fn document_create_sends_input_variable() { + use lineark_sdk::generated::inputs::DocumentCreateInput; + + let (server, client) = setup_mutation("documentCreate").await; + let input = DocumentCreateInput { + title: Some("Test Document".to_string()), + content: Some("# Hello".to_string()), + ..Default::default() + }; + let _ = client.document_create::(input).await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["input"]["title"], "Test Document"); + assert_eq!(vars["input"]["content"], "# Hello"); +} + +#[tokio::test] +async fn document_update_sends_input_and_id() { + use lineark_sdk::generated::inputs::DocumentUpdateInput; + + let (server, client) = setup_mutation("documentUpdate").await; + let input = DocumentUpdateInput { + title: Some("Updated Title".to_string()), + ..Default::default() + }; + let _ = client + .document_update::(input, "doc-uuid-123".to_string()) + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["input"]["title"], "Updated Title"); + assert_eq!(vars["id"], "doc-uuid-123"); +} + +#[tokio::test] +async fn document_delete_sends_id() { + let (server, client) = setup_mutation("documentDelete").await; + let _ = client + .document_delete::("doc-uuid-456".to_string()) + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["id"], "doc-uuid-456"); +} + +#[tokio::test] +async fn issue_relation_create_sends_input() { + use lineark_sdk::generated::enums::IssueRelationType; + use lineark_sdk::generated::inputs::IssueRelationCreateInput; + + let (server, client) = setup_mutation("issueRelationCreate").await; + let input = IssueRelationCreateInput { + issue_id: Some("issue-a".to_string()), + related_issue_id: Some("issue-b".to_string()), + r#type: Some(IssueRelationType::Blocks), + ..Default::default() + }; + let _ = client + .issue_relation_create::(None, input) + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["input"]["issueId"], "issue-a"); + assert_eq!(vars["input"]["relatedIssueId"], "issue-b"); + assert_eq!(vars["input"]["type"], "blocks"); + assert_eq!(vars["overrideCreatedAt"], Value::Null); +} + +#[tokio::test] +async fn file_upload_sends_required_params() { + let (server, client) = setup_mutation("fileUpload").await; + let _ = client + .file_upload( + None, + Some(true), + 1024, + "image/png".to_string(), + "screenshot.png".to_string(), + ) + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["size"], 1024); + assert_eq!(vars["contentType"], "image/png"); + assert_eq!(vars["filename"], "screenshot.png"); + assert_eq!(vars["makePublic"], true); + assert_eq!(vars["metaData"], Value::Null); +} + +#[tokio::test] +async fn image_upload_from_url_sends_url() { + let (server, client) = setup_mutation("imageUploadFromUrl").await; + let _ = client + .image_upload_from_url("https://example.com/image.png".to_string()) + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["url"], "https://example.com/image.png"); +} + +#[tokio::test] +async fn issue_archive_sends_id_and_trash() { + let (server, client) = setup_mutation("issueArchive").await; + let _ = client + .issue_archive::(Some(true), "issue-uuid-arch".to_string()) + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["id"], "issue-uuid-arch"); + assert_eq!(vars["trash"], true); +} + +#[tokio::test] +async fn issue_archive_without_trash_sends_null() { + let (server, client) = setup_mutation("issueArchive").await; + let _ = client + .issue_archive::(None, "issue-uuid-arch2".to_string()) + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["id"], "issue-uuid-arch2"); + assert_eq!(vars["trash"], Value::Null); +} + +#[tokio::test] +async fn issue_unarchive_sends_id() { + let (server, client) = setup_mutation("issueUnarchive").await; + let _ = client + .issue_unarchive::("issue-uuid-unarch".to_string()) + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["id"], "issue-uuid-unarch"); +} + +#[tokio::test] +async fn issue_delete_sends_id_and_permanently_delete() { + let (server, client) = setup_mutation("issueDelete").await; + let _ = client + .issue_delete::(Some(true), "issue-uuid-123".to_string()) + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["id"], "issue-uuid-123"); + assert_eq!(vars["permanentlyDelete"], true); +} + +#[tokio::test] +async fn issue_delete_without_permanently_sends_null() { + let (server, client) = setup_mutation("issueDelete").await; + let _ = client + .issue_delete::(None, "issue-uuid-456".to_string()) + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["id"], "issue-uuid-456"); + assert_eq!(vars["permanentlyDelete"], Value::Null); +} diff --git a/crates/lineark-sdk/tests/offline/queries.rs b/crates/lineark-sdk/tests/offline/queries.rs new file mode 100644 index 0000000..f6cb076 --- /dev/null +++ b/crates/lineark-sdk/tests/offline/queries.rs @@ -0,0 +1,378 @@ +use super::*; + +// ── TeamsQueryBuilder ─────────────────────────────────────────────────────────────── + +#[tokio::test] +async fn teams_first_sets_variable() { + let (server, client) = setup("teams").await; + let _ = client.teams::().first(42).send().await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["first"], 42); + assert_eq!(vars["last"], Value::Null); +} + +#[tokio::test] +async fn teams_last_sets_variable() { + let (server, client) = setup("teams").await; + let _ = client.teams::().last(7).send().await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["last"], 7); + assert_eq!(vars["first"], Value::Null); +} + +#[tokio::test] +async fn teams_before_after_set_variables() { + let (server, client) = setup("teams").await; + let _ = client + .teams::() + .before("cursor-abc") + .after("cursor-xyz") + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["before"], "cursor-abc"); + assert_eq!(vars["after"], "cursor-xyz"); +} + +#[tokio::test] +async fn teams_include_archived_sets_variable() { + let (server, client) = setup("teams").await; + let _ = client.teams::().include_archived(true).send().await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["includeArchived"], true); +} + +#[tokio::test] +async fn teams_all_params_chain() { + let (server, client) = setup("teams").await; + let _ = client + .teams::() + .first(10) + .after("cur") + .include_archived(false) + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["first"], 10); + assert_eq!(vars["after"], "cur"); + assert_eq!(vars["includeArchived"], false); + assert_eq!(vars["before"], Value::Null); + assert_eq!(vars["last"], Value::Null); +} + +#[tokio::test] +async fn teams_no_params_sends_all_null() { + let (server, client) = setup("teams").await; + let _ = client.teams::().send().await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["first"], Value::Null); + assert_eq!(vars["last"], Value::Null); + assert_eq!(vars["before"], Value::Null); + assert_eq!(vars["after"], Value::Null); + assert_eq!(vars["includeArchived"], Value::Null); +} + +// ── UsersQueryBuilder ─────────────────────────────────────────────────────────────── + +#[tokio::test] +async fn users_include_disabled_sets_variable() { + let (server, client) = setup("users").await; + let _ = client + .users::() + .include_disabled(true) + .last(5) + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["includeDisabled"], true); + assert_eq!(vars["last"], 5); +} + +// ── SearchIssuesQueryBuilder (has required `term` arg) ────────────────────────────── + +#[tokio::test] +async fn search_issues_term_is_required() { + let (server, client) = setup("searchIssues").await; + let _ = client + .search_issues::("my query") + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["term"], "my query"); +} + +#[tokio::test] +async fn search_issues_all_optional_params() { + let (server, client) = setup("searchIssues").await; + let _ = client + .search_issues::("bug") + .first(20) + .include_comments(true) + .team_id("team-uuid-123") + .include_archived(true) + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["term"], "bug"); + assert_eq!(vars["first"], 20); + assert_eq!(vars["includeComments"], true); + assert_eq!(vars["teamId"], "team-uuid-123"); + assert_eq!(vars["includeArchived"], true); + // Unset optionals are null. + assert_eq!(vars["last"], Value::Null); + assert_eq!(vars["before"], Value::Null); + assert_eq!(vars["after"], Value::Null); +} + +#[tokio::test] +async fn search_issues_string_setters_accept_str_ref() { + let (server, client) = setup("searchIssues").await; + // All string setters should accept &str (via impl Into). + let _ = client + .search_issues::("term") + .before("b") + .after("a") + .team_id("t") + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["term"], "term"); + assert_eq!(vars["before"], "b"); + assert_eq!(vars["after"], "a"); + assert_eq!(vars["teamId"], "t"); +} + +// ── IssuesQueryBuilder ────────────────────────────────────────────────────────────── + +#[tokio::test] +async fn issues_first_and_include_archived() { + let (server, client) = setup("issues").await; + let _ = client + .issues::() + .first(100) + .include_archived(true) + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["first"], 100); + assert_eq!(vars["includeArchived"], true); +} + +// ── CyclesQueryBuilder ────────────────────────────────────────────────────────────── + +#[tokio::test] +async fn cycles_first_sets_variable() { + let (server, client) = setup("cycles").await; + let _ = client.cycles::().first(50).send().await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["first"], 50); +} + +// ── IssueLabelsQueryBuilder ───────────────────────────────────────────────────────── + +#[tokio::test] +async fn issue_labels_first_sets_variable() { + let (server, client) = setup("issueLabels").await; + let _ = client.issue_labels::().first(250).send().await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["first"], 250); +} + +// ── ProjectsQueryBuilder ──────────────────────────────────────────────────────────── + +#[tokio::test] +async fn projects_last_and_before() { + let (server, client) = setup("projects").await; + let _ = client + .projects::() + .last(25) + .before("cur-end") + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["last"], 25); + assert_eq!(vars["before"], "cur-end"); +} + +// ── WorkflowStatesQueryBuilder ────────────────────────────────────────────────────── + +#[tokio::test] +async fn workflow_states_first_sets_variable() { + let (server, client) = setup("workflowStates").await; + let _ = client + .workflow_states::() + .first(50) + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["first"], 50); +} + +// ── DocumentsQueryBuilder ────────────────────────────────────────────────────────── + +#[tokio::test] +async fn documents_first_sets_variable() { + let (server, client) = setup("documents").await; + let _ = client.documents::().first(20).send().await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["first"], 20); +} + +#[tokio::test] +async fn documents_last_sets_variable() { + let (server, client) = setup("documents").await; + let _ = client.documents::().last(8).send().await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["last"], 8); + assert_eq!(vars["first"], Value::Null); +} + +#[tokio::test] +async fn documents_after_sets_variable() { + let (server, client) = setup("documents").await; + let _ = client + .documents::() + .first(10) + .after("cursor-abc") + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["after"], "cursor-abc"); +} + +#[tokio::test] +async fn documents_before_sets_variable() { + let (server, client) = setup("documents").await; + let _ = client + .documents::() + .before("cursor-end") + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["before"], "cursor-end"); +} + +#[tokio::test] +async fn documents_include_archived_sets_variable() { + let (server, client) = setup("documents").await; + let _ = client + .documents::() + .include_archived(true) + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["includeArchived"], true); +} + +#[tokio::test] +async fn documents_all_params_chain() { + let (server, client) = setup("documents").await; + let _ = client + .documents::() + .first(15) + .after("cur-start") + .include_archived(true) + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["first"], 15); + assert_eq!(vars["after"], "cur-start"); + assert_eq!(vars["includeArchived"], true); + assert_eq!(vars["before"], Value::Null); + assert_eq!(vars["last"], Value::Null); +} + +#[tokio::test] +async fn documents_no_params_sends_all_null() { + let (server, client) = setup("documents").await; + let _ = client.documents::().send().await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["first"], Value::Null); + assert_eq!(vars["last"], Value::Null); + assert_eq!(vars["before"], Value::Null); + assert_eq!(vars["after"], Value::Null); + assert_eq!(vars["includeArchived"], Value::Null); +} + +// ── IssueRelationsQueryBuilder ───────────────────────────────────────────────────── + +#[tokio::test] +async fn issue_relations_first_sets_variable() { + let (server, client) = setup("issueRelations").await; + let _ = client + .issue_relations::() + .first(25) + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["first"], 25); +} + +#[tokio::test] +async fn issue_relations_last_sets_variable() { + let (server, client) = setup("issueRelations").await; + let _ = client + .issue_relations::() + .last(3) + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["last"], 3); + assert_eq!(vars["first"], Value::Null); +} + +#[tokio::test] +async fn issue_relations_before_after_set_variables() { + let (server, client) = setup("issueRelations").await; + let _ = client + .issue_relations::() + .before("cursor-b") + .after("cursor-a") + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["before"], "cursor-b"); + assert_eq!(vars["after"], "cursor-a"); +} + +#[tokio::test] +async fn issue_relations_include_archived_sets_variable() { + let (server, client) = setup("issueRelations").await; + let _ = client + .issue_relations::() + .include_archived(true) + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["includeArchived"], true); +} + +#[tokio::test] +async fn issue_relations_all_params_chain() { + let (server, client) = setup("issueRelations").await; + let _ = client + .issue_relations::() + .first(30) + .after("rel-cursor") + .include_archived(false) + .send() + .await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["first"], 30); + assert_eq!(vars["after"], "rel-cursor"); + assert_eq!(vars["includeArchived"], false); + assert_eq!(vars["before"], Value::Null); + assert_eq!(vars["last"], Value::Null); +} + +#[tokio::test] +async fn issue_relations_no_params_sends_all_null() { + let (server, client) = setup("issueRelations").await; + let _ = client.issue_relations::().send().await; + let vars = extract_variables(&server.received_requests().await.unwrap()); + assert_eq!(vars["first"], Value::Null); + assert_eq!(vars["last"], Value::Null); + assert_eq!(vars["before"], Value::Null); + assert_eq!(vars["after"], Value::Null); + assert_eq!(vars["includeArchived"], Value::Null); +} diff --git a/crates/lineark/tests/offline.rs b/crates/lineark/tests/offline.rs index e6a65ad..1eb6af6 100644 --- a/crates/lineark/tests/offline.rs +++ b/crates/lineark/tests/offline.rs @@ -1,7 +1,4 @@ -//! Offline CLI tests for lineark. -//! -//! These tests don't need any API token — they test usage, help, and error handling. -//! Online tests that hit the real Linear API live in `cli_online.rs`. +//! Offline CLI tests for lineark — no API token needed. use assert_cmd::Command; use predicates::prelude::*; @@ -11,735 +8,29 @@ fn lineark() -> Command { Command::cargo_bin("lineark").unwrap() } -// ── Usage command ─────────────────────────────────────────────────────────── - -#[test] -fn usage_prints_command_reference() { - lineark() - .arg("usage") - .assert() - .success() - .stdout(predicate::str::contains("lineark")) - .stdout(predicate::str::contains("COMMANDS")) - .stdout(predicate::str::contains("whoami")) - .stdout(predicate::str::contains("teams")) - .stdout(predicate::str::contains("issues")) - .stdout(predicate::str::contains("AUTH")); -} - -#[test] -fn usage_mentions_global_options() { - lineark() - .arg("usage") - .assert() - .success() - .stdout(predicate::str::contains("--api-token")) - .stdout(predicate::str::contains("--format")); -} - -// ── Help flags ────────────────────────────────────────────────────────────── - -#[test] -fn help_flag_shows_help() { - lineark() - .arg("--help") - .assert() - .success() - .stdout(predicate::str::contains("lineark")) - .stdout(predicate::str::contains("Usage")); -} - -#[test] -fn version_flag_shows_version() { - lineark() - .arg("--version") - .assert() - .success() - .stdout(predicate::str::contains("lineark")); -} - -#[test] -fn teams_help_shows_subcommands() { - lineark() - .args(["teams", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("list")); -} - -#[test] -fn issues_help_shows_subcommands() { - lineark() - .args(["issues", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("list")) - .stdout(predicate::str::contains("read")) - .stdout(predicate::str::contains("search")) - .stdout(predicate::str::contains("create")) - .stdout(predicate::str::contains("update")) - .stdout(predicate::str::contains("archive")) - .stdout(predicate::str::contains("unarchive")) - .stdout(predicate::str::contains("delete")); -} - -#[test] -fn issues_create_help_shows_flags() { - lineark() - .args(["issues", "create", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--team")) - .stdout(predicate::str::contains("--priority")) - .stdout(predicate::str::contains("--description")) - .stdout(predicate::str::contains("--assignee")) - .stdout(predicate::str::contains("--labels")); -} - -#[test] -fn issues_update_help_shows_flags() { - lineark() - .args(["issues", "update", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--status")) - .stdout(predicate::str::contains("--priority")) - .stdout(predicate::str::contains("--labels")) - .stdout(predicate::str::contains("--label-by")) - .stdout(predicate::str::contains("--clear-labels")) - .stdout(predicate::str::contains("--assignee")) - .stdout(predicate::str::contains("--parent")); -} - -#[test] -fn comments_help_shows_subcommands() { - lineark() - .args(["comments", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("create")); -} - -#[test] -fn comments_create_help_shows_flags() { - lineark() - .args(["comments", "create", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--body")) - .stdout(predicate::str::contains("")); -} - -#[test] -fn usage_includes_write_commands() { - lineark() - .arg("usage") - .assert() - .success() - .stdout(predicate::str::contains("issues create")) - .stdout(predicate::str::contains("issues update")) - .stdout(predicate::str::contains("issues archive")) - .stdout(predicate::str::contains("issues unarchive")) - .stdout(predicate::str::contains("issues delete")) - .stdout(predicate::str::contains("comments create")); -} - -#[test] -fn issues_archive_help_shows_identifier() { - lineark() - .args(["issues", "archive", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("")); -} - -#[test] -fn issues_unarchive_help_shows_identifier() { - lineark() - .args(["issues", "unarchive", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("")); -} - -#[test] -fn issues_delete_help_shows_flags() { - lineark() - .args(["issues", "delete", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--permanently")) - .stdout(predicate::str::contains("")); -} - -// ── Documents ─────────────────────────────────────────────────────────────── - -#[test] -fn documents_help_shows_subcommands() { - lineark() - .args(["documents", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("list")) - .stdout(predicate::str::contains("read")) - .stdout(predicate::str::contains("create")) - .stdout(predicate::str::contains("update")) - .stdout(predicate::str::contains("delete")); -} - -#[test] -fn documents_create_help_shows_flags() { - lineark() - .args(["documents", "create", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--title")) - .stdout(predicate::str::contains("--content")) - .stdout(predicate::str::contains("--project")) - .stdout(predicate::str::contains("--issue")); -} - -#[test] -fn documents_update_help_shows_flags() { - lineark() - .args(["documents", "update", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--title")) - .stdout(predicate::str::contains("--content")); -} - -// ── Embeds ────────────────────────────────────────────────────────────────── - -#[test] -fn embeds_help_shows_subcommands() { - lineark() - .args(["embeds", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("download")) - .stdout(predicate::str::contains("upload")); -} - -#[test] -fn embeds_download_help_shows_flags() { - lineark() - .args(["embeds", "download", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--output")) - .stdout(predicate::str::contains("--overwrite")); -} - -#[test] -fn embeds_upload_help_shows_flags() { - lineark() - .args(["embeds", "upload", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--public")); -} - -// ── Cycles ────────────────────────────────────────────────────────────────── - -#[test] -fn cycles_help_shows_subcommands() { - lineark() - .args(["cycles", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("list")) - .stdout(predicate::str::contains("read")); -} - -#[test] -fn cycles_list_help_shows_flags() { - lineark() - .args(["cycles", "list", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--active")) - .stdout(predicate::str::contains("--team")) - .stdout(predicate::str::contains("--around-active")) - .stdout(predicate::str::contains("--limit")); -} - -#[test] -fn cycles_read_help_shows_team_flag() { - lineark() - .args(["cycles", "read", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--team")); -} - -// ── Usage includes Phase 3 commands ───────────────────────────────────────── - -#[test] -fn usage_includes_documents_commands() { - lineark() - .arg("usage") - .assert() - .success() - .stdout(predicate::str::contains("documents list")) - .stdout(predicate::str::contains("documents read")) - .stdout(predicate::str::contains("documents create")) - .stdout(predicate::str::contains("documents update")) - .stdout(predicate::str::contains("documents delete")); -} - -#[test] -fn usage_includes_embeds_commands() { - lineark() - .arg("usage") - .assert() - .success() - .stdout(predicate::str::contains("embeds download")) - .stdout(predicate::str::contains("embeds upload")); -} - -#[test] -fn usage_includes_cycles_flags() { - lineark() - .arg("usage") - .assert() - .success() - .stdout(predicate::str::contains("--active")) - .stdout(predicate::str::contains("--around-active")); -} - -// ── Labels ────────────────────────────────────────────────────────────────── - -#[test] -fn labels_list_help_shows_team_flag() { - lineark() - .args(["labels", "list", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--team")); -} - -// ── Auth error handling ───────────────────────────────────────────────────── - -#[test] -fn invalid_token_prints_error_and_exits_nonzero() { - lineark() - .args(["--api-token", "invalid_token_abc", "whoami"]) - .assert() - .failure() - .stderr(predicate::str::contains("Error")); -} - -#[test] -fn empty_token_prints_error() { - lineark() - .args(["--api-token", "", "whoami"]) - .assert() - .failure() - .stderr(predicate::str::contains("Error")); -} - -// ── --active and --around-active conflict (issue #4) ──────────────────────── - -#[test] -fn cycles_list_active_and_around_active_conflict() { - lineark() - .args(["cycles", "list", "--active", "--around-active", "2"]) - .assert() - .failure() - .stderr(predicate::str::contains("cannot be used with")); -} - -// ── No-op update validation (issue #9) ────────────────────────────────────── - -#[test] -fn issues_update_no_flags_prints_error() { - // Use a fake token to skip auth, but the validation should fire before any API call - lineark() - .args(["--api-token", "fake-token", "issues", "update", "ENG-123"]) - .assert() - .failure() - .stderr(predicate::str::contains("No update fields provided")); -} - -#[test] -fn documents_update_no_flags_prints_error() { - lineark() - .args([ - "--api-token", - "fake-token", - "documents", - "update", - "doc-uuid", - ]) - .assert() - .failure() - .stderr(predicate::str::contains("No update fields provided")); -} - -// ── Projects ──────────────────────────────────────────────────────────────── - -#[test] -fn projects_help_shows_subcommands() { - lineark() - .args(["projects", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("list")) - .stdout(predicate::str::contains("read")) - .stdout(predicate::str::contains("create")); -} - -#[test] -fn projects_create_help_shows_flags() { - lineark() - .args(["projects", "create", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--team")) - .stdout(predicate::str::contains("--description")) - .stdout(predicate::str::contains("--lead")) - .stdout(predicate::str::contains("--members")) - .stdout(predicate::str::contains("--start-date")) - .stdout(predicate::str::contains("--target-date")) - .stdout(predicate::str::contains("--priority")) - .stdout(predicate::str::contains("--content")) - .stdout(predicate::str::contains("--icon")) - .stdout(predicate::str::contains("--color")); -} - -#[test] -fn projects_list_help_shows_led_by_me_flag() { - lineark() - .args(["projects", "list", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--led-by-me")); -} - -#[test] -fn projects_read_help_shows_description() { - lineark() - .args(["projects", "read", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("Project name or UUID")); -} - -#[test] -fn projects_create_requires_team_flag() { - lineark() - .args([ - "--api-token", - "fake-token", - "projects", - "create", - "My Project", - ]) - .assert() - .failure() - .stderr(predicate::str::contains("--team")); -} - -#[test] -fn usage_includes_projects_create() { - lineark() - .arg("usage") - .assert() - .success() - .stdout(predicate::str::contains("projects create")); -} - -// ── Project milestones ────────────────────────────────────────────────────── - -#[test] -fn milestones_help_shows_subcommands() { - lineark() - .args(["project-milestones", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("list")) - .stdout(predicate::str::contains("read")) - .stdout(predicate::str::contains("create")) - .stdout(predicate::str::contains("update")) - .stdout(predicate::str::contains("delete")); -} - -#[test] -fn milestones_list_help_shows_project_flag() { - lineark() - .args(["project-milestones", "list", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--project")) - .stdout(predicate::str::contains("--limit")); -} - -#[test] -fn milestones_create_help_shows_flags() { - lineark() - .args(["project-milestones", "create", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--project")) - .stdout(predicate::str::contains("--target-date")) - .stdout(predicate::str::contains("--description")); -} - -#[test] -fn milestones_update_help_shows_flags() { - lineark() - .args(["project-milestones", "update", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--project")) - .stdout(predicate::str::contains("--name")) - .stdout(predicate::str::contains("--target-date")) - .stdout(predicate::str::contains("--description")); -} - -#[test] -fn milestones_update_no_flags_prints_error() { - lineark() - .args([ - "--api-token", - "fake-token", - "project-milestones", - "update", - "some-uuid", - ]) - .assert() - .failure() - .stderr(predicate::str::contains("No update fields provided")); -} - -#[test] -fn usage_includes_milestones_commands() { - lineark() - .arg("usage") - .assert() - .success() - .stdout(predicate::str::contains("project-milestones list")) - .stdout(predicate::str::contains("project-milestones read")) - .stdout(predicate::str::contains("project-milestones create")) - .stdout(predicate::str::contains("project-milestones update")) - .stdout(predicate::str::contains("project-milestones delete")); -} - -// ── Issues: new flags ────────────────────────────────────────────────────── - -#[test] -fn issues_create_help_shows_project_and_cycle() { - lineark() - .args(["issues", "create", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--project")) - .stdout(predicate::str::contains("--cycle")) - .stdout(predicate::str::contains("--status")); -} - -#[test] -fn issues_update_help_shows_clear_parent_and_project() { - lineark() - .args(["issues", "update", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--clear-parent")) - .stdout(predicate::str::contains("--project")) - .stdout(predicate::str::contains("--cycle")); -} - -#[test] -fn issues_search_help_shows_filter_flags() { - lineark() - .args(["issues", "search", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--team")) - .stdout(predicate::str::contains("--assignee")) - .stdout(predicate::str::contains("--status")); -} - -#[test] -fn issues_update_clear_parent_conflicts_with_parent() { - lineark() - .args([ - "--api-token", - "fake", - "issues", - "update", - "ENG-123", - "--parent", - "ENG-456", - "--clear-parent", - ]) - .assert() - .failure() - .stderr(predicate::str::contains("cannot be used with")); -} - -// ── Documents: filter flags ──────────────────────────────────────────────── - -#[test] -fn documents_list_help_shows_filter_flags() { - lineark() - .args(["documents", "list", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--project")) - .stdout(predicate::str::contains("--issue")); -} - -// ── Short flag aliases ───────────────────────────────────────────────────── - -#[test] -fn issues_list_accepts_short_limit_flag() { - // -l should be accepted as --limit alias (will fail on API call, but parsing should succeed) - lineark() - .args(["--api-token", "fake", "issues", "list", "-l", "5"]) - .assert() - .failure() // fails on API, not on arg parsing - .stderr(predicate::str::contains("limit").not()); // should not complain about limit flag -} - -#[test] -fn issues_create_accepts_short_flags() { - lineark() - .args(["issues", "create", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("-p")) - .stdout(predicate::str::contains("-d")) - .stdout(predicate::str::contains("-s")); -} - -#[test] -fn issues_update_accepts_short_flags() { - lineark() - .args(["issues", "update", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("-p")) - .stdout(predicate::str::contains("-d")) - .stdout(predicate::str::contains("-t")) - .stdout(predicate::str::contains("-s")); -} - -// ── Usage includes name resolution info ──────────────────────────────────── - -#[test] -fn usage_includes_name_resolution() { - lineark() - .arg("usage") - .assert() - .success() - .stdout(predicate::str::contains("NAME RESOLUTION")); -} - -#[test] -fn usage_includes_me_alias() { - lineark() - .arg("usage") - .assert() - .success() - .stdout(predicate::str::contains("`me`")); -} - -#[test] -fn usage_includes_projects_read() { - lineark() - .arg("usage") - .assert() - .success() - .stdout(predicate::str::contains("projects read")); -} - -#[test] -fn usage_includes_led_by_me() { - lineark() - .arg("usage") - .assert() - .success() - .stdout(predicate::str::contains("--led-by-me")); -} - -#[test] -fn usage_includes_members_flag() { - lineark() - .arg("usage") - .assert() - .success() - .stdout(predicate::str::contains("--members")); -} - -// ── Cycle number parsing rejects NaN/inf (issue #6) ───────────────────────── - -#[test] -fn cycles_read_rejects_nan() { - // NaN should not parse as i64, so it will be treated as a name lookup - // which requires --team. This verifies it doesn't silently succeed as f64. - lineark() - .args(["--api-token", "fake", "cycles", "read", "NaN"]) - .assert() - .failure() - .stderr(predicate::str::contains("--team")); -} - -#[test] -fn cycles_read_rejects_inf() { - lineark() - .args(["--api-token", "fake", "cycles", "read", "inf"]) - .assert() - .failure() - .stderr(predicate::str::contains("--team")); -} - -// ── Self command ───────────────────────────────────────────────────────────── - -#[test] -fn self_help_shows_update_subcommand() { - lineark() - .args(["self", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("update")); -} - -#[test] -fn self_update_help_shows_check_flag() { - lineark() - .args(["self", "update", "--help"]) - .assert() - .success() - .stdout(predicate::str::contains("--check")); -} - -#[test] -fn self_update_dev_build_exits_cleanly() { - // Dev builds have version 0.0.0 — self update should print a message and succeed. - lineark() - .args(["self", "update"]) - .assert() - .success() - .stderr(predicate::str::contains("dev build")); -} - -#[test] -fn self_update_check_dev_build_exits_cleanly() { - lineark() - .args(["self", "update", "--check"]) - .assert() - .success() - .stderr(predicate::str::contains("dev build")); -} - -#[test] -fn usage_includes_self_update_commands() { - lineark() - .arg("usage") - .assert() - .success() - .stdout(predicate::str::contains("self update")) - .stdout(predicate::str::contains("--check")); -} +#[path = "offline/auth.rs"] +mod auth; +#[path = "offline/comments.rs"] +mod comments; +#[path = "offline/cycles.rs"] +mod cycles; +#[path = "offline/documents.rs"] +mod documents; +#[path = "offline/embeds.rs"] +mod embeds; +#[path = "offline/format.rs"] +mod format; +#[path = "offline/issues.rs"] +mod issues; +#[path = "offline/labels.rs"] +mod labels; +#[path = "offline/milestones.rs"] +mod milestones; +#[path = "offline/projects.rs"] +mod projects; +#[path = "offline/self_update.rs"] +mod self_update; +#[path = "offline/teams.rs"] +mod teams; +#[path = "offline/usage.rs"] +mod usage; diff --git a/crates/lineark/tests/offline/auth.rs b/crates/lineark/tests/offline/auth.rs new file mode 100644 index 0000000..cfa3907 --- /dev/null +++ b/crates/lineark/tests/offline/auth.rs @@ -0,0 +1,19 @@ +use super::*; + +#[test] +fn invalid_token_prints_error_and_exits_nonzero() { + lineark() + .args(["--api-token", "invalid_token_abc", "whoami"]) + .assert() + .failure() + .stderr(predicate::str::contains("Error")); +} + +#[test] +fn empty_token_prints_error() { + lineark() + .args(["--api-token", "", "whoami"]) + .assert() + .failure() + .stderr(predicate::str::contains("Error")); +} diff --git a/crates/lineark/tests/offline/comments.rs b/crates/lineark/tests/offline/comments.rs new file mode 100644 index 0000000..7dc0c96 --- /dev/null +++ b/crates/lineark/tests/offline/comments.rs @@ -0,0 +1,20 @@ +use super::*; + +#[test] +fn comments_help_shows_subcommands() { + lineark() + .args(["comments", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("create")); +} + +#[test] +fn comments_create_help_shows_flags() { + lineark() + .args(["comments", "create", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--body")) + .stdout(predicate::str::contains("")); +} diff --git a/crates/lineark/tests/offline/cycles.rs b/crates/lineark/tests/offline/cycles.rs new file mode 100644 index 0000000..b2cbfcb --- /dev/null +++ b/crates/lineark/tests/offline/cycles.rs @@ -0,0 +1,59 @@ +use super::*; + +#[test] +fn cycles_help_shows_subcommands() { + lineark() + .args(["cycles", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("list")) + .stdout(predicate::str::contains("read")); +} + +#[test] +fn cycles_list_help_shows_flags() { + lineark() + .args(["cycles", "list", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--active")) + .stdout(predicate::str::contains("--team")) + .stdout(predicate::str::contains("--around-active")) + .stdout(predicate::str::contains("--limit")); +} + +#[test] +fn cycles_read_help_shows_team_flag() { + lineark() + .args(["cycles", "read", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--team")); +} + +#[test] +fn cycles_list_active_and_around_active_conflict() { + lineark() + .args(["cycles", "list", "--active", "--around-active", "2"]) + .assert() + .failure() + .stderr(predicate::str::contains("cannot be used with")); +} + +#[test] +fn cycles_read_rejects_nan() { + lineark() + .args(["--api-token", "fake", "cycles", "read", "NaN"]) + .assert() + .failure() + .stderr(predicate::str::contains("--team")); +} + +#[test] +fn cycles_read_rejects_inf() { + lineark() + .args(["--api-token", "fake", "cycles", "read", "inf"]) + .assert() + .failure() + .stderr(predicate::str::contains("--team")); +} diff --git a/crates/lineark/tests/offline/documents.rs b/crates/lineark/tests/offline/documents.rs new file mode 100644 index 0000000..b7a1846 --- /dev/null +++ b/crates/lineark/tests/offline/documents.rs @@ -0,0 +1,61 @@ +use super::*; + +#[test] +fn documents_help_shows_subcommands() { + lineark() + .args(["documents", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("list")) + .stdout(predicate::str::contains("read")) + .stdout(predicate::str::contains("create")) + .stdout(predicate::str::contains("update")) + .stdout(predicate::str::contains("delete")); +} + +#[test] +fn documents_create_help_shows_flags() { + lineark() + .args(["documents", "create", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--title")) + .stdout(predicate::str::contains("--content")) + .stdout(predicate::str::contains("--project")) + .stdout(predicate::str::contains("--issue")); +} + +#[test] +fn documents_update_help_shows_flags() { + lineark() + .args(["documents", "update", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--title")) + .stdout(predicate::str::contains("--content")); +} + +#[test] +fn documents_update_no_flags_prints_error() { + lineark() + .args([ + "--api-token", + "fake-token", + "documents", + "update", + "doc-uuid", + ]) + .assert() + .failure() + .stderr(predicate::str::contains("No update fields provided")); +} + +#[test] +fn documents_list_help_shows_filter_flags() { + lineark() + .args(["documents", "list", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--project")) + .stdout(predicate::str::contains("--issue")); +} diff --git a/crates/lineark/tests/offline/embeds.rs b/crates/lineark/tests/offline/embeds.rs new file mode 100644 index 0000000..91fdc93 --- /dev/null +++ b/crates/lineark/tests/offline/embeds.rs @@ -0,0 +1,30 @@ +use super::*; + +#[test] +fn embeds_help_shows_subcommands() { + lineark() + .args(["embeds", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("download")) + .stdout(predicate::str::contains("upload")); +} + +#[test] +fn embeds_download_help_shows_flags() { + lineark() + .args(["embeds", "download", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--output")) + .stdout(predicate::str::contains("--overwrite")); +} + +#[test] +fn embeds_upload_help_shows_flags() { + lineark() + .args(["embeds", "upload", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--public")); +} diff --git a/crates/lineark/tests/offline/format.rs b/crates/lineark/tests/offline/format.rs new file mode 100644 index 0000000..dbc425e --- /dev/null +++ b/crates/lineark/tests/offline/format.rs @@ -0,0 +1,34 @@ +use super::*; + +#[test] +fn issues_list_accepts_short_limit_flag() { + // -l should be accepted as --limit alias (will fail on API call, but parsing should succeed) + lineark() + .args(["--api-token", "fake", "issues", "list", "-l", "5"]) + .assert() + .failure() // fails on API, not on arg parsing + .stderr(predicate::str::contains("limit").not()); // should not complain about limit flag +} + +#[test] +fn issues_create_accepts_short_flags() { + lineark() + .args(["issues", "create", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("-p")) + .stdout(predicate::str::contains("-d")) + .stdout(predicate::str::contains("-s")); +} + +#[test] +fn issues_update_accepts_short_flags() { + lineark() + .args(["issues", "update", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("-p")) + .stdout(predicate::str::contains("-d")) + .stdout(predicate::str::contains("-t")) + .stdout(predicate::str::contains("-s")); +} diff --git a/crates/lineark/tests/offline/issues.rs b/crates/lineark/tests/offline/issues.rs new file mode 100644 index 0000000..fba35ab --- /dev/null +++ b/crates/lineark/tests/offline/issues.rs @@ -0,0 +1,133 @@ +use super::*; + +#[test] +fn issues_help_shows_subcommands() { + lineark() + .args(["issues", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("list")) + .stdout(predicate::str::contains("read")) + .stdout(predicate::str::contains("search")) + .stdout(predicate::str::contains("create")) + .stdout(predicate::str::contains("update")) + .stdout(predicate::str::contains("archive")) + .stdout(predicate::str::contains("unarchive")) + .stdout(predicate::str::contains("delete")); +} + +#[test] +fn issues_create_help_shows_flags() { + lineark() + .args(["issues", "create", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--team")) + .stdout(predicate::str::contains("--priority")) + .stdout(predicate::str::contains("--description")) + .stdout(predicate::str::contains("--assignee")) + .stdout(predicate::str::contains("--labels")); +} + +#[test] +fn issues_update_help_shows_flags() { + lineark() + .args(["issues", "update", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--status")) + .stdout(predicate::str::contains("--priority")) + .stdout(predicate::str::contains("--labels")) + .stdout(predicate::str::contains("--label-by")) + .stdout(predicate::str::contains("--clear-labels")) + .stdout(predicate::str::contains("--assignee")) + .stdout(predicate::str::contains("--parent")); +} + +#[test] +fn issues_archive_help_shows_identifier() { + lineark() + .args(["issues", "archive", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("")); +} + +#[test] +fn issues_unarchive_help_shows_identifier() { + lineark() + .args(["issues", "unarchive", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("")); +} + +#[test] +fn issues_delete_help_shows_flags() { + lineark() + .args(["issues", "delete", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--permanently")) + .stdout(predicate::str::contains("")); +} + +#[test] +fn issues_update_no_flags_prints_error() { + lineark() + .args(["--api-token", "fake-token", "issues", "update", "ENG-123"]) + .assert() + .failure() + .stderr(predicate::str::contains("No update fields provided")); +} + +#[test] +fn issues_create_help_shows_project_and_cycle() { + lineark() + .args(["issues", "create", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--project")) + .stdout(predicate::str::contains("--cycle")) + .stdout(predicate::str::contains("--status")); +} + +#[test] +fn issues_update_help_shows_clear_parent_and_project() { + lineark() + .args(["issues", "update", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--clear-parent")) + .stdout(predicate::str::contains("--project")) + .stdout(predicate::str::contains("--cycle")); +} + +#[test] +fn issues_search_help_shows_filter_flags() { + lineark() + .args(["issues", "search", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--team")) + .stdout(predicate::str::contains("--assignee")) + .stdout(predicate::str::contains("--status")); +} + +#[test] +fn issues_update_clear_parent_conflicts_with_parent() { + lineark() + .args([ + "--api-token", + "fake", + "issues", + "update", + "ENG-123", + "--parent", + "ENG-456", + "--clear-parent", + ]) + .assert() + .failure() + .stderr(predicate::str::contains("cannot be used with")); +} diff --git a/crates/lineark/tests/offline/labels.rs b/crates/lineark/tests/offline/labels.rs new file mode 100644 index 0000000..a9f4f02 --- /dev/null +++ b/crates/lineark/tests/offline/labels.rs @@ -0,0 +1,10 @@ +use super::*; + +#[test] +fn labels_list_help_shows_team_flag() { + lineark() + .args(["labels", "list", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--team")); +} diff --git a/crates/lineark/tests/offline/milestones.rs b/crates/lineark/tests/offline/milestones.rs new file mode 100644 index 0000000..57254c9 --- /dev/null +++ b/crates/lineark/tests/offline/milestones.rs @@ -0,0 +1,62 @@ +use super::*; + +#[test] +fn milestones_help_shows_subcommands() { + lineark() + .args(["project-milestones", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("list")) + .stdout(predicate::str::contains("read")) + .stdout(predicate::str::contains("create")) + .stdout(predicate::str::contains("update")) + .stdout(predicate::str::contains("delete")); +} + +#[test] +fn milestones_list_help_shows_project_flag() { + lineark() + .args(["project-milestones", "list", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--project")) + .stdout(predicate::str::contains("--limit")); +} + +#[test] +fn milestones_create_help_shows_flags() { + lineark() + .args(["project-milestones", "create", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--project")) + .stdout(predicate::str::contains("--target-date")) + .stdout(predicate::str::contains("--description")); +} + +#[test] +fn milestones_update_help_shows_flags() { + lineark() + .args(["project-milestones", "update", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--project")) + .stdout(predicate::str::contains("--name")) + .stdout(predicate::str::contains("--target-date")) + .stdout(predicate::str::contains("--description")); +} + +#[test] +fn milestones_update_no_flags_prints_error() { + lineark() + .args([ + "--api-token", + "fake-token", + "project-milestones", + "update", + "some-uuid", + ]) + .assert() + .failure() + .stderr(predicate::str::contains("No update fields provided")); +} diff --git a/crates/lineark/tests/offline/projects.rs b/crates/lineark/tests/offline/projects.rs new file mode 100644 index 0000000..6d013de --- /dev/null +++ b/crates/lineark/tests/offline/projects.rs @@ -0,0 +1,63 @@ +use super::*; + +#[test] +fn projects_help_shows_subcommands() { + lineark() + .args(["projects", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("list")) + .stdout(predicate::str::contains("read")) + .stdout(predicate::str::contains("create")); +} + +#[test] +fn projects_create_help_shows_flags() { + lineark() + .args(["projects", "create", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--team")) + .stdout(predicate::str::contains("--description")) + .stdout(predicate::str::contains("--lead")) + .stdout(predicate::str::contains("--members")) + .stdout(predicate::str::contains("--start-date")) + .stdout(predicate::str::contains("--target-date")) + .stdout(predicate::str::contains("--priority")) + .stdout(predicate::str::contains("--content")) + .stdout(predicate::str::contains("--icon")) + .stdout(predicate::str::contains("--color")); +} + +#[test] +fn projects_list_help_shows_led_by_me_flag() { + lineark() + .args(["projects", "list", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--led-by-me")); +} + +#[test] +fn projects_read_help_shows_description() { + lineark() + .args(["projects", "read", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("Project name or UUID")); +} + +#[test] +fn projects_create_requires_team_flag() { + lineark() + .args([ + "--api-token", + "fake-token", + "projects", + "create", + "My Project", + ]) + .assert() + .failure() + .stderr(predicate::str::contains("--team")); +} diff --git a/crates/lineark/tests/offline/self_update.rs b/crates/lineark/tests/offline/self_update.rs new file mode 100644 index 0000000..d94775d --- /dev/null +++ b/crates/lineark/tests/offline/self_update.rs @@ -0,0 +1,37 @@ +use super::*; + +#[test] +fn self_help_shows_update_subcommand() { + lineark() + .args(["self", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("update")); +} + +#[test] +fn self_update_help_shows_check_flag() { + lineark() + .args(["self", "update", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("--check")); +} + +#[test] +fn self_update_dev_build_exits_cleanly() { + lineark() + .args(["self", "update"]) + .assert() + .success() + .stderr(predicate::str::contains("dev build")); +} + +#[test] +fn self_update_check_dev_build_exits_cleanly() { + lineark() + .args(["self", "update", "--check"]) + .assert() + .success() + .stderr(predicate::str::contains("dev build")); +} diff --git a/crates/lineark/tests/offline/teams.rs b/crates/lineark/tests/offline/teams.rs new file mode 100644 index 0000000..b147b1e --- /dev/null +++ b/crates/lineark/tests/offline/teams.rs @@ -0,0 +1,10 @@ +use super::*; + +#[test] +fn teams_help_shows_subcommands() { + lineark() + .args(["teams", "--help"]) + .assert() + .success() + .stdout(predicate::str::contains("list")); +} diff --git a/crates/lineark/tests/offline/usage.rs b/crates/lineark/tests/offline/usage.rs new file mode 100644 index 0000000..ddf2514 --- /dev/null +++ b/crates/lineark/tests/offline/usage.rs @@ -0,0 +1,168 @@ +use super::*; + +#[test] +fn usage_prints_command_reference() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("lineark")) + .stdout(predicate::str::contains("COMMANDS")) + .stdout(predicate::str::contains("whoami")) + .stdout(predicate::str::contains("teams")) + .stdout(predicate::str::contains("issues")) + .stdout(predicate::str::contains("AUTH")); +} + +#[test] +fn usage_mentions_global_options() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("--api-token")) + .stdout(predicate::str::contains("--format")); +} + +#[test] +fn help_flag_shows_help() { + lineark() + .arg("--help") + .assert() + .success() + .stdout(predicate::str::contains("lineark")) + .stdout(predicate::str::contains("Usage")); +} + +#[test] +fn version_flag_shows_version() { + lineark() + .arg("--version") + .assert() + .success() + .stdout(predicate::str::contains("lineark")); +} + +#[test] +fn usage_includes_write_commands() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("issues create")) + .stdout(predicate::str::contains("issues update")) + .stdout(predicate::str::contains("issues archive")) + .stdout(predicate::str::contains("issues unarchive")) + .stdout(predicate::str::contains("issues delete")) + .stdout(predicate::str::contains("comments create")); +} + +#[test] +fn usage_includes_documents_commands() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("documents list")) + .stdout(predicate::str::contains("documents read")) + .stdout(predicate::str::contains("documents create")) + .stdout(predicate::str::contains("documents update")) + .stdout(predicate::str::contains("documents delete")); +} + +#[test] +fn usage_includes_embeds_commands() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("embeds download")) + .stdout(predicate::str::contains("embeds upload")); +} + +#[test] +fn usage_includes_cycles_flags() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("--active")) + .stdout(predicate::str::contains("--around-active")); +} + +#[test] +fn usage_includes_projects_create() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("projects create")); +} + +#[test] +fn usage_includes_milestones_commands() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("project-milestones list")) + .stdout(predicate::str::contains("project-milestones read")) + .stdout(predicate::str::contains("project-milestones create")) + .stdout(predicate::str::contains("project-milestones update")) + .stdout(predicate::str::contains("project-milestones delete")); +} + +#[test] +fn usage_includes_name_resolution() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("NAME RESOLUTION")); +} + +#[test] +fn usage_includes_me_alias() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("`me`")); +} + +#[test] +fn usage_includes_projects_read() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("projects read")); +} + +#[test] +fn usage_includes_led_by_me() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("--led-by-me")); +} + +#[test] +fn usage_includes_members_flag() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("--members")); +} + +#[test] +fn usage_includes_self_update_commands() { + lineark() + .arg("usage") + .assert() + .success() + .stdout(predicate::str::contains("self update")) + .stdout(predicate::str::contains("--check")); +}