diff --git a/src/import/mod.rs b/src/import/mod.rs index 2f708d0..894c65a 100644 --- a/src/import/mod.rs +++ b/src/import/mod.rs @@ -24,6 +24,9 @@ mod stack; mod stacks; mod supervisor; +#[cfg(test)] +mod tests; + pub(crate) async fn run(ctx: &FullCtx) -> color_eyre::Result<()> { // get list of trello boards let trello_client = ctx.trello_client(); diff --git a/src/import/tests/mod.rs b/src/import/tests/mod.rs new file mode 100644 index 0000000..d9dcd10 --- /dev/null +++ b/src/import/tests/mod.rs @@ -0,0 +1,413 @@ +// +use http::StatusCode; +use kameo::actor::spawn_in_thread; +use kxio::{fs::TempFileSystem, net::MockNet}; +use rstest::fixture; +use serde_json::json; +use tokio::time::Instant; + +use crate::{ + config::AppConfig, + import::{ + labels::LabelsActor, rate_limit::RateLimitActor, stacks::ImportStacksActor, + supervisor::Supervisor, + }, + nextcloud::{ + model::{NextcloudBoardId, NextcloudStackId}, + NextcloudConfig, + }, + rate_limit::RateLimit, + s, spawn_in_thread, + trello::{ + model::{ + card::TrelloShortCard, + list::{TrelloList, TrelloListPosition}, + }, + TrelloConfig, + }, + FullCtx, +}; + +#[fixture] +fn ctx_fs() -> (FullCtx, TempFileSystem) { + let fs = kxio::fs::temp().expect("temp fs"); + let mock_net = kxio::net::mock(); + ( + FullCtx { + fs: fs.as_real(), + temp_fs: fs.clone(), + net: mock_net.into(), + prt: kxio::print::standard(), + cfg: AppConfig { + trello: TrelloConfig { + api_key: crate::s!("trello-api-key").into(), + api_secret: crate::s!("trello-api-secret").into(), + }, + nextcloud: NextcloudConfig { + hostname: crate::s!("https://nextcloud-hostname").into(), + username: crate::s!("nextcloud-username").into(), + password: crate::s!("nextcloud-password").into(), + }, + }, + }, + fs, + ) +} + +#[fixture] +fn trello_card_without_description_or_label() -> TrelloShortCard { + TrelloShortCard { + id: s!("trello-id").into(), + name: s!("trello-name").into(), + desc: s!("").into(), + labels: vec![], + pos: 333.into(), + } +} + +#[fixture] +fn board_id() -> NextcloudBoardId { + 444.into() +} + +#[fixture] +fn stack_id() -> NextcloudStackId { + 555.into() +} + +#[fixture] +fn limiter() -> RateLimit { + RateLimit::new("test", 100, 0.0, Instant::now()) +} + +#[fixture] +fn trello_stack() -> TrelloList { + TrelloList { + id: s!("trello-list-id").into(), + name: s!("new-list-name").into(), + pos: TrelloListPosition(3), + } +} + +#[rstest::rstest] +#[test_log::test(tokio::test(flavor = "multi_thread", worker_threads = 1))] +async fn test_create_card_with_description_label_and_attachment( + ctx_fs: (FullCtx, TempFileSystem), + trello_stack: TrelloList, + board_id: NextcloudBoardId, + limiter: RateLimit, +) { + let (mut ctx, _temp_fs) = ctx_fs; + + //given + { + let mock_net = MockNet::try_from(ctx.net.clone()).await.expect("mock net"); + + // LabelsActor + given_nextcloud_get_labels_for_board(&mock_net); + given_nextcloud_create_label(&mock_net); + + // ImportStacksActor + given_nextcloud_get_stacks_for_board(&mock_net); + given_nextcloud_create_stack(&mock_net); + given_nextcloud_get_stacks_for_board_with_new_stack(&mock_net); + given_trello_get_stack(&mock_net); + + // ImportCardActor + given_nextcloud_create_card_with_description(&mock_net); + given_trello_get_card_for_attachments_list(&mock_net); + + // ImportLabelActor + given_nextcloud_add_label_to_card(&mock_net); + + // ImportAttachmentActor + given_trello_get_details_of_attachment(&mock_net); + given_trello_save_attachment(&mock_net); + given_nextcloud_upload_attachment(&mock_net); + + ctx.net = mock_net.into(); + } + + let supervisor = spawn_in_thread(Supervisor); + + // given_nextcloud_get_labels_for_board + let labels_actor_ref = spawn_in_thread!(supervisor, LabelsActor::new(ctx.clone(), board_id)); + + let limiter = spawn_in_thread!(supervisor, RateLimitActor::new(limiter)); + + let trello_stack_name = trello_stack.name.clone(); + let trello_stacks: Vec = vec![trello_stack]; + + //when + // given_nextcloud_create_card_with_description + // given_trello_get_card_for_attachments_list + // spawns: label actor + // given_nextcloud_create_label + // given_nextcloud_add_label_to_card + // spawns: attachment actor + // given_trello_get_details_of_attachment + // given_trello_save_attachment + // given_nextcloud_upload_attachment + let _stacks = spawn_in_thread!( + supervisor, + ImportStacksActor::new( + ctx.clone(), + trello_stacks, + vec![trello_stack_name.into()], + board_id, + labels_actor_ref, + limiter, + ) + ); + + //then + tokio::select! { + _ = tokio::time::sleep(std::time::Duration::from_secs(1)) => {panic!("timeout")}, + _ = supervisor.wait_for_stop() => {} + } + + ctx.net.assert_no_unused_plans(); +} + +#[rstest::rstest] +#[test_log::test(tokio::test(flavor = "multi_thread", worker_threads = 1))] +async fn test_create_card_without_description_label_or_attachment( + ctx_fs: (FullCtx, TempFileSystem), + trello_stack: TrelloList, + board_id: NextcloudBoardId, + limiter: RateLimit, +) { + let (mut ctx, _temp_fs) = ctx_fs; + + //given + { + let mock_net = MockNet::try_from(ctx.net.clone()).await.expect("mock net"); + + // LabelsActor + given_nextcloud_get_labels_for_board(&mock_net); + + // ImportStacksActor + given_nextcloud_get_stacks_for_board(&mock_net); + given_nextcloud_create_stack(&mock_net); + given_nextcloud_get_stacks_for_board_with_new_stack(&mock_net); + given_trello_get_stack_with_card_with_no_description_or_label(&mock_net); + + // ImportCardActor + given_nextcloud_create_card_without_description(&mock_net); + given_trello_get_card_for_empty_attachments_list(&mock_net); + + ctx.net = mock_net.into(); + } + + let supervisor = spawn_in_thread(Supervisor); + + // requires: get_board (i.e. given_get_labels_for_board_from_nextcloud) + // // nextcloud get labels for board: GET boards/{board_id} + let labels_actor_ref = spawn_in_thread!(supervisor, LabelsActor::new(ctx.clone(), board_id)); + + let limiter = spawn_in_thread!(supervisor, RateLimitActor::new(limiter)); + + let trello_stack_name = trello_stack.name.clone(); + let trello_stacks: Vec = vec![trello_stack]; + + //when + let _stacks = spawn_in_thread!( + supervisor, + ImportStacksActor::new( + ctx.clone(), + trello_stacks, + vec![trello_stack_name.into()], + board_id, + labels_actor_ref, + limiter, + ) + ); + + //then + tokio::select! { + _ = tokio::time::sleep(std::time::Duration::from_secs(1)) => {panic!("timeout")}, + _ = supervisor.wait_for_stop() => {} + } + + ctx.net.assert_no_unused_plans(); +} + +fn given_nextcloud_upload_attachment(mock_net: &MockNet) { + // upload attachment to Nextcloud + mock_net + .on() + .post("https://nextcloud-hostname/index.php/apps/deck/api/v1.0/boards/444/stacks/555/cards/331/attachments") + .respond(StatusCode::OK) + .body(include_bytes!("../../tests/responses/nextcloud-attachment-add.json").as_slice()) + .expect("mock request create attachment"); +} + +fn given_nextcloud_add_label_to_card(mock_net: &MockNet) { + mock_net + .on() + .put("https://nextcloud-hostname/index.php/apps/deck/api/v1.0/boards/444/stacks/555/cards/331/assignLabel") + .body(json!({ + "labelId": 54 + }).to_string()) + .respond(StatusCode::OK) + .mock() + .expect("mock request create attachment"); +} + +fn given_trello_get_card_for_attachments_list(mock_net: &MockNet) { + mock_net + .on() + .get("https://api.trello.com/1/cards/65ad94865aed24f70ecdcebb") + .query("attachments", "true") + .header("content-type", "application/json") + .header("accept", "application/json") + .respond(StatusCode::OK) + .body(include_bytes!("../../tests/responses/trello-card-get.json").as_slice()) + .expect("mock request create attachment"); +} + +fn given_trello_get_card_for_empty_attachments_list(mock_net: &MockNet) { + mock_net + .on() + .get("https://api.trello.com/1/cards/65ad94865aed24f70ecdcebb") + .query("attachments", "true") + .header("content-type", "application/json") + .header("accept", "application/json") + .respond(StatusCode::OK) + .body( + include_bytes!("../../tests/responses/trello-card-get-no-description.json").as_slice(), + ) + .expect("mock request create attachment"); +} + +fn given_trello_save_attachment(mock_net: &MockNet) { + // save attachment from Trello + mock_net.on() + .get("https://trello.com/1/cards/65ad94865aed24f70ecdcebb/attachments/65ad94875aed24f70ecdd037/download/Backlog.png") + .respond(StatusCode::OK) + .body(include_bytes!("../../tests/responses/trello-attachment-save.png").as_slice()) + .expect("mock save attachment"); +} + +fn given_trello_get_details_of_attachment(mock_net: &MockNet) { + // get details of attachment from Trello + mock_net + .on() + .get("https://api.trello.com/1/cards/65ad94865aed24f70ecdcebb/attachments/65ad94875aed24f70ecdd037") + .respond(StatusCode::OK) + .body(include_bytes!("../../tests/responses/trello-attachment-get.json").as_slice()) + .expect("mock request attachment"); +} + +fn given_nextcloud_get_labels_for_board(mock_net: &MockNet) { + // get labels for the board from nextcloud + mock_net + .on() + .get("https://nextcloud-hostname/index.php/apps/deck/api/v1.0/boards/444") + .respond(StatusCode::OK) + .body(include_bytes!("../../tests/responses/nextcloud-board-labels.json").as_slice()) + .expect("mock request board"); +} + +fn given_nextcloud_create_card_with_description(mock_net: &MockNet) { + // create card in Nextcloud + mock_net + .on() + .post("https://nextcloud-hostname/index.php/apps/deck/api/v1.0/boards/444/stacks/555/cards") + .body( + json!({ + "description": "A list of the things we think we want to do, maybe not quite ready for work, but high likelihood of being worked on.\n\nThis is the staging area where specs should get fleshed out.\n\nNo limit on the list size, but we should reconsider if it gets long.", + "title": "Backlog", + }) // TODO: (#10) include card 'order' from trello 'pos' + .to_string(), + ) + .respond(StatusCode::OK) + .body(include_bytes!("../../tests/responses/nextcloud-card-create.json").as_slice()) + .expect("mock request create card"); +} + +fn given_nextcloud_create_card_without_description(mock_net: &MockNet) { + // create card in Nextcloud + mock_net + .on() + .post("https://nextcloud-hostname/index.php/apps/deck/api/v1.0/boards/444/stacks/555/cards") + .body( + json!({ + // "description": "trello-desc", + "title": "Backlog", + }) // TODO: (#10) include card 'order' from trello 'pos' + .to_string(), + ) + .respond(StatusCode::OK) + .body(include_bytes!("../../tests/responses/nextcloud-card-create.json").as_slice()) + .expect("mock request create card"); +} + +fn given_nextcloud_create_label(mock_net: &MockNet) { + // create card in Nextcloud + mock_net + .on() + .post("https://nextcloud-hostname/index.php/apps/deck/api/v1.0/boards/444/labels") + .body( + json!({ + "title": "Green", + "color": "00ff00", // from green + }) + .to_string(), + ) + .respond(StatusCode::OK) + .body(include_bytes!("../../tests/responses/nextcloud-board-create-label.json").as_slice()) + .expect("mock request create label"); +} + +fn given_nextcloud_get_stacks_for_board(mock_net: &MockNet) { + mock_net + .on() + .get("https://nextcloud-hostname/index.php/apps/deck/api/v1.0/boards/444/stacks") + .respond(StatusCode::OK) + .body(include_bytes!("../../tests/responses/nextcloud-board-get.json").as_slice()) + .expect("mock request get stacks"); +} + +fn given_nextcloud_get_stacks_for_board_with_new_stack(mock_net: &MockNet) { + mock_net + .on() + .get("https://nextcloud-hostname/index.php/apps/deck/api/v1.0/boards/444/stacks") + .respond(StatusCode::OK) + .body( + include_bytes!("../../tests/responses/nextcloud-board-get-with-new-stack.json") + .as_slice(), + ) + .expect("mock request get stacks"); +} + +fn given_nextcloud_create_stack(mock_net: &MockNet) { + // + mock_net + .on() + .post("https://nextcloud-hostname/index.php/apps/deck/api/v1.0/boards/444/stacks") + .respond(StatusCode::OK) + .body(include_bytes!("../../tests/responses/nextcloud-stack-create.json").as_slice()) + .expect("mock request create stack"); +} + +fn given_trello_get_stack(mock_net: &MockNet) { + mock_net + .on() + .get("https://api.trello.com/1/lists/trello-list-id/cards") + .respond(StatusCode::OK) + .body(include_bytes!("../../tests/responses/trello-list-get.json").as_slice()) + .expect("mock get stack"); +} + +fn given_trello_get_stack_with_card_with_no_description_or_label(mock_net: &MockNet) { + mock_net + .on() + .get("https://api.trello.com/1/lists/trello-list-id/cards") + .respond(StatusCode::OK) + .body( + include_bytes!("../../tests/responses/trello-list-get-no-desc-or-label.json") + .as_slice(), + ) + .expect("mock get stack"); +} diff --git a/src/nextcloud/tests/card/add_attachment.rs b/src/nextcloud/tests/card/add_attachment.rs index 4f532bc..3527c45 100644 --- a/src/nextcloud/tests/card/add_attachment.rs +++ b/src/nextcloud/tests/card/add_attachment.rs @@ -45,10 +45,7 @@ fn ctx_path() -> (FullCtx, PathBuf, TempFileSystem) { "{hostname}/index.php/apps/deck/api/v1.0/boards/{board_id}/stacks/{stack_id}/cards/{card_id}/attachments", )) .respond(StatusCode::OK) - .body(serde_json::to_string(&Attachment { - id: 102, - attachment_type: "file".to_string(), - }).expect("json attachment")) + .body(include_bytes!("../../../tests/responses/nextcloud-attachment-add.json").as_slice()) .expect("mock request"); ( diff --git a/src/tests/responses/nextcloud-attachment-add.json b/src/tests/responses/nextcloud-attachment-add.json index 0ea7e47..87d742b 100644 --- a/src/tests/responses/nextcloud-attachment-add.json +++ b/src/tests/responses/nextcloud-attachment-add.json @@ -1 +1,5 @@ -{"id":102,"type":"file"} \ No newline at end of file +{ + "foo": "nextcloud-attachment-add.json", + "id": 102, + "type": "file" +} \ No newline at end of file diff --git a/src/tests/responses/nextcloud-board-create-label.json b/src/tests/responses/nextcloud-board-create-label.json index 3f4204e..db5dd04 100644 --- a/src/tests/responses/nextcloud-board-create-label.json +++ b/src/tests/responses/nextcloud-board-create-label.json @@ -1,4 +1,5 @@ { + "foo": "nextcloud-board-create-label.json", "id": 54, "title": "my green label", "color": "31CC7C", diff --git a/src/tests/responses/nextcloud-board-get-with-new-stack.json b/src/tests/responses/nextcloud-board-get-with-new-stack.json new file mode 100644 index 0000000..db4c592 --- /dev/null +++ b/src/tests/responses/nextcloud-board-get-with-new-stack.json @@ -0,0 +1,301 @@ +[ + { + "foo": "nextcloud-board-get-with-new-stack.json", + "id": 555, + "title": "new-list-name", + "boardId": 1, + "deletedAt": 0, + "lastModified": 1733515991, + "cards": [], + "order": 4, + "ETag": "fda386b3b247d7b4bd8917e19d38c01b" + }, + { + "id": 3, + "title": "Done", + "boardId": 1, + "deletedAt": 0, + "lastModified": 1733515991, + "cards": [ + { + "id": 322, + "title": "Lunch: Soup & Toast", + "description": "", + "stackId": 3, + "type": "plain", + "lastModified": 1733515991, + "lastEditor": null, + "createdAt": 1733043472, + "labels": [ + { + "id": 4, + "title": "Later", + "color": "F1DB50", + "boardId": 1, + "cardId": 322, + "lastModified": 1670965629, + "ETag": "983f87848dc9c18d0aee63e7ee0fc83f" + } + ], + "assignedUsers": [ + { + "id": 25, + "participant": { + "primaryKey": "pcampbell", + "uid": "pcampbell", + "displayname": "Paul Campbell", + "type": 0 + }, + "cardId": 322, + "type": 0 + } + ], + "attachments": null, + "attachmentCount": 0, + "owner": { + "primaryKey": "pcampbell", + "uid": "pcampbell", + "displayname": "Paul Campbell", + "type": 0 + }, + "order": 0, + "archived": false, + "done": null, + "duedate": null, + "deletedAt": 0, + "commentsUnread": 0, + "commentsCount": 0, + "ETag": "dda386b3b247d7b4bd8917e19d38c01b", + "overdue": 0 + } + ], + "order": 2, + "ETag": "dda386b3b247d7b4bd8917e19d38c01b" + }, + { + "id": 2, + "title": "Doing", + "boardId": 1, + "deletedAt": 0, + "lastModified": 1733695323, + "cards": [ + { + "id": 319, + "title": "That", + "description": "", + "stackId": 2, + "type": "plain", + "lastModified": 1733335979, + "lastEditor": null, + "createdAt": 1732610551, + "labels": [], + "assignedUsers": [], + "attachments": null, + "attachmentCount": 1, + "owner": { + "primaryKey": "pcampbell", + "uid": "pcampbell", + "displayname": "Paul Campbell", + "type": 0 + }, + "order": 0, + "archived": false, + "done": null, + "duedate": null, + "deletedAt": 0, + "commentsUnread": 0, + "commentsCount": 0, + "ETag": "79aeb703494e67736cc66b35053d258d", + "overdue": 0 + }, + { + "id": 323, + "title": "Second lunch: Poached Egg & Toasted Muffin", + "description": "", + "stackId": 2, + "type": "plain", + "lastModified": 1733695323, + "lastEditor": null, + "createdAt": 1733043481, + "labels": [], + "assignedUsers": [ + { + "id": 26, + "participant": { + "primaryKey": "pcampbell", + "uid": "pcampbell", + "displayname": "Paul Campbell", + "type": 0 + }, + "cardId": 323, + "type": 0 + } + ], + "attachments": null, + "attachmentCount": 0, + "owner": { + "primaryKey": "pcampbell", + "uid": "pcampbell", + "displayname": "Paul Campbell", + "type": 0 + }, + "order": 1, + "archived": false, + "done": null, + "duedate": null, + "deletedAt": 0, + "commentsUnread": 0, + "commentsCount": 0, + "ETag": "9de45fc7d68507f1eaef462e90e6414c", + "overdue": 0 + } + ], + "order": 1, + "ETag": "9de45fc7d68507f1eaef462e90e6414c" + }, + { + "id": 1, + "title": "To do", + "boardId": 1, + "deletedAt": 0, + "lastModified": 1734115321, + "cards": [ + { + "id": 318, + "title": "This", + "description": "", + "stackId": 1, + "type": "plain", + "lastModified": 1733049748, + "lastEditor": null, + "createdAt": 1732610548, + "labels": [], + "assignedUsers": [], + "attachments": null, + "attachmentCount": 1, + "owner": { + "primaryKey": "pcampbell", + "uid": "pcampbell", + "displayname": "Paul Campbell", + "type": 0 + }, + "order": 0, + "archived": false, + "done": null, + "duedate": null, + "deletedAt": 0, + "commentsUnread": 0, + "commentsCount": 0, + "ETag": "e5007451d88799e3e3d3581cbcb30210", + "overdue": 0 + }, + { + "id": 321, + "title": "Breakfast: Cereal", + "description": "", + "stackId": 1, + "type": "plain", + "lastModified": 1734115321, + "lastEditor": null, + "createdAt": 1733043461, + "labels": [ + { + "id": 1, + "title": "Finished", + "color": "31CC7C", + "boardId": 1, + "cardId": 321, + "lastModified": 1670965629, + "ETag": "983f87848dc9c18d0aee63e7ee0fc83f" + }, + { + "id": 1, + "title": "Finished", + "color": "31CC7C", + "boardId": 1, + "cardId": 321, + "lastModified": 1670965629, + "ETag": "983f87848dc9c18d0aee63e7ee0fc83f" + }, + { + "id": 1, + "title": "Finished", + "color": "31CC7C", + "boardId": 1, + "cardId": 321, + "lastModified": 1670965629, + "ETag": "983f87848dc9c18d0aee63e7ee0fc83f" + }, + { + "id": 2, + "title": "To review", + "color": "317CCC", + "boardId": 1, + "cardId": 321, + "lastModified": 1670965629, + "ETag": "983f87848dc9c18d0aee63e7ee0fc83f" + }, + { + "id": 2, + "title": "To review", + "color": "317CCC", + "boardId": 1, + "cardId": 321, + "lastModified": 1670965629, + "ETag": "983f87848dc9c18d0aee63e7ee0fc83f" + }, + { + "id": 3, + "title": "Action needed", + "color": "FF7A66", + "boardId": 1, + "cardId": 321, + "lastModified": 1670965629, + "ETag": "983f87848dc9c18d0aee63e7ee0fc83f" + }, + { + "id": 4, + "title": "Later", + "color": "F1DB50", + "boardId": 1, + "cardId": 321, + "lastModified": 1670965629, + "ETag": "983f87848dc9c18d0aee63e7ee0fc83f" + } + ], + "assignedUsers": [ + { + "id": 24, + "participant": { + "primaryKey": "pcampbell", + "uid": "pcampbell", + "displayname": "Paul Campbell", + "type": 0 + }, + "cardId": 321, + "type": 0 + } + ], + "attachments": null, + "attachmentCount": 0, + "owner": { + "primaryKey": "pcampbell", + "uid": "pcampbell", + "displayname": "Paul Campbell", + "type": 0 + }, + "order": 1, + "archived": false, + "done": null, + "duedate": null, + "deletedAt": 0, + "commentsUnread": 0, + "commentsCount": 0, + "ETag": "9acdd42135c0347968891d2a073b87be", + "overdue": 0 + } + ], + "order": 0, + "ETag": "9acdd42135c0347968891d2a073b87be" + } +] diff --git a/src/tests/responses/nextcloud-board-get.json b/src/tests/responses/nextcloud-board-get.json index ff155d9..6036246 100644 --- a/src/tests/responses/nextcloud-board-get.json +++ b/src/tests/responses/nextcloud-board-get.json @@ -1,5 +1,6 @@ [ { + "foo": "nextcloud-board-get.json", "id": 3, "title": "Done", "boardId": 1, diff --git a/src/tests/responses/nextcloud-board-labels.json b/src/tests/responses/nextcloud-board-labels.json index 78d0aaf..f832434 100644 --- a/src/tests/responses/nextcloud-board-labels.json +++ b/src/tests/responses/nextcloud-board-labels.json @@ -1,4 +1,5 @@ { + "foo": "nextcloud-board-labels.json", "id": 1, "title": "Personal Board", "owner": { diff --git a/src/tests/responses/nextcloud-board-list.json b/src/tests/responses/nextcloud-board-list.json index 29cadc4..23b729f 100644 --- a/src/tests/responses/nextcloud-board-list.json +++ b/src/tests/responses/nextcloud-board-list.json @@ -1,5 +1,6 @@ [ { + "foo": "nextcloud-board-list.json", "id": 1, "title": "Personal Board", "owner": { diff --git a/src/tests/responses/nextcloud-boards.json b/src/tests/responses/nextcloud-boards.json index d28ee80..6843bf5 100644 --- a/src/tests/responses/nextcloud-boards.json +++ b/src/tests/responses/nextcloud-boards.json @@ -1,5 +1,6 @@ [ { + "foo": "nextcloud-boards.json", "id": 1, "title": "Personal Board", "owner": { diff --git a/src/tests/responses/nextcloud-card-create.json b/src/tests/responses/nextcloud-card-create.json index 26fa62f..1f4bb87 100644 --- a/src/tests/responses/nextcloud-card-create.json +++ b/src/tests/responses/nextcloud-card-create.json @@ -1,4 +1,5 @@ { + "foo": "nextcloud-card-create.json", "id": 331, "title": "my new card", "description": "my new description", diff --git a/src/tests/responses/nextcloud-card-get.json b/src/tests/responses/nextcloud-card-get.json index 7ed1ca5..d62ee26 100644 --- a/src/tests/responses/nextcloud-card-get.json +++ b/src/tests/responses/nextcloud-card-get.json @@ -1,4 +1,5 @@ { + "foo": "nextcloud-card-get.json", "id": 321, "title": "Breakfast: Cereal", "description": "", diff --git a/src/tests/responses/nextcloud-deck-get.json b/src/tests/responses/nextcloud-deck-get.json index cf5faff..a034319 100644 --- a/src/tests/responses/nextcloud-deck-get.json +++ b/src/tests/responses/nextcloud-deck-get.json @@ -1,5 +1,6 @@ [ { + "foo": "nextcloud-deck-get.json", "id": 1, "title": "Personal Board", "owner": { diff --git a/src/tests/responses/nextcloud-stack-create.json b/src/tests/responses/nextcloud-stack-create.json index 1d2216c..7db0c83 100644 --- a/src/tests/responses/nextcloud-stack-create.json +++ b/src/tests/responses/nextcloud-stack-create.json @@ -1,4 +1,5 @@ { + "foo": "nextcloud-stack-create.json", "id": 30, "title": "Incoming", "boardId": 1, diff --git a/src/tests/responses/nextcloud-stack-get.json b/src/tests/responses/nextcloud-stack-get.json index 412dd55..10c07f7 100644 --- a/src/tests/responses/nextcloud-stack-get.json +++ b/src/tests/responses/nextcloud-stack-get.json @@ -1,4 +1,5 @@ { + "foo": "nextcloud-stack-get.json", "id": 3, "title": "Done", "boardId": 1, diff --git a/src/tests/responses/nextcloud-stack-list-2.json b/src/tests/responses/nextcloud-stack-list-2.json index c576c6c..82f01a8 100644 --- a/src/tests/responses/nextcloud-stack-list-2.json +++ b/src/tests/responses/nextcloud-stack-list-2.json @@ -1,5 +1,6 @@ [ { + "foo": "nextcloud-stack-list-2.json", "id": 1, "title": "To do", "boardId": 1, diff --git a/src/tests/responses/nextcloud-stack-list.json b/src/tests/responses/nextcloud-stack-list.json index a9b42bd..5b2588f 100644 --- a/src/tests/responses/nextcloud-stack-list.json +++ b/src/tests/responses/nextcloud-stack-list.json @@ -1,5 +1,6 @@ [ { + "foo": "nextcloud-stack-list.json", "id": 3, "title": "Done", "boardId": 1, diff --git a/src/tests/responses/trello-attachment-get.json b/src/tests/responses/trello-attachment-get.json index 8e74324..96d07c1 100644 --- a/src/tests/responses/trello-attachment-get.json +++ b/src/tests/responses/trello-attachment-get.json @@ -1,4 +1,5 @@ { + "foo": "trello-attachment-get.json", "id": "65ad94875aed24f70ecdd037", "bytes": 184198, "date": "2019-01-02T22:47:17.325Z", diff --git a/src/tests/responses/trello-board-get.json b/src/tests/responses/trello-board-get.json index 94d938a..0f3947e 100644 --- a/src/tests/responses/trello-board-get.json +++ b/src/tests/responses/trello-board-get.json @@ -1,4 +1,5 @@ { + "foo": "trello-board-get.json", "id": "65ad94865aed24f70ecdce4b", "name": "Tasyn Kanban", "desc": "Use this simple Kanban template to keep the engineering team on the same page and moving through work fluidly. \n\n1. Break down the roadmap by adding tasks as cards to the **Backlog** list. \n\n2. Move the cards one-by-one through **Design** as they becomes more fleshed out. *Pro tip:* You can enable Power-ups for your favorite design tools like [Figma](https://trello.com/power-ups/59b2e7611e6ece0b35eac16a/figma) or [Invision](https://trello.com/power-ups/596f2cb2d279152540b2bb31), in order to easily link and view designs without switching context.\n\n3. When a card is fully specced out and designs are attached, move it to **To Do** for engineers to pick up. \n\n4. Engineers move cards to **Doing** and assign themselves to the cards, so the whole team stays informed of who is working on what.\n\n5. Cards then move through **Code Review** when they're ready for a second set of eyes. The team can set a **List Limit** (with the List Limit Power-up) on the number of cards in Code Review, as a visual indicator for when the team needs to prioritize reviews rather than picking up new work. \n\n6. Once cards move through **Testing** and eventually ship to production, move them to **Done** and celebrate!\n", diff --git a/src/tests/responses/trello-boards-list.json b/src/tests/responses/trello-boards-list.json index fbfe39e..a8974f4 100644 --- a/src/tests/responses/trello-boards-list.json +++ b/src/tests/responses/trello-boards-list.json @@ -1,5 +1,6 @@ [ { + "foo": "trello-boards-list.json", "id": "5db72d5517a6135e166fd862", "nodeId": "ari:cloud:trello::board/workspace/60ae020570a89b46695aae66/5db72d5517a6135e166fd862", "name": "0 Business: Cossmass Infinities", diff --git a/src/tests/responses/trello-boards.json b/src/tests/responses/trello-boards.json index 5d17d40..2e0a189 100644 --- a/src/tests/responses/trello-boards.json +++ b/src/tests/responses/trello-boards.json @@ -1,5 +1,6 @@ [ { + "foo": "trello-boards.json", "id": "5abbe4b7ddc1b351ef961414", "name": "Trello Platform Changes", "desc": "Track changes to Trello's Platform on this board.", diff --git a/src/tests/responses/trello-card-get-no-description.json b/src/tests/responses/trello-card-get-no-description.json new file mode 100644 index 0000000..3336664 --- /dev/null +++ b/src/tests/responses/trello-card-get-no-description.json @@ -0,0 +1,67 @@ +{ + "foo": "trello-card-get-no-description.json", + "id": "65ad94865aed24f70ecdced1", + "badges": { + "attachmentsByType": { + "trello": { + "board": 0, + "card": 0 + } + }, + "externalSource": null, + "location": false, + "votes": 0, + "viewingMemberVoted": false, + "subscribed": false, + "fogbugz": "", + "checkItems": 0, + "checkItemsChecked": 0, + "checkItemsEarliestDue": null, + "comments": 0, + "attachments": 0, + "description": false, + "due": null, + "dueComplete": false, + "start": null, + "lastUpdatedByAi": false + }, + "checkItemStates": [], + "closed": false, + "dueComplete": false, + "dateLastActivity": "2024-12-25T08:40:22.054Z", + "desc": "", + "descData": null, + "due": null, + "dueReminder": null, + "email": null, + "idBoard": "65ad94865aed24f70ecdce4b", + "idChecklists": [], + "idList": "65ad94865aed24f70ecdce4c", + "idMembers": [], + "idMembersVoted": [], + "idShort": 12, + "idAttachmentCover": null, + "labels": [], + "idLabels": [], + "manualCoverAttachment": false, + "name": "Task - no description, labels or attachments", + "pinned": false, + "pos": 81920, + "shortLink": "kqHXwwev", + "shortUrl": "https://trello.com/c/kqHXwwev", + "start": null, + "subscribed": false, + "url": "https://trello.com/c/kqHXwwev/12-task-no-description-labels-or-attachments", + "cover": { + "idAttachment": null, + "color": null, + "idUploadedBackground": null, + "size": "normal", + "brightness": "light", + "idPlugin": null + }, + "isTemplate": false, + "cardRole": null, + "mirrorSourceId": null, + "attachments": [] +} diff --git a/src/tests/responses/trello-card-get.json b/src/tests/responses/trello-card-get.json index 26b68d7..3461c0b 100644 --- a/src/tests/responses/trello-card-get.json +++ b/src/tests/responses/trello-card-get.json @@ -1,4 +1,5 @@ { + "foo": "trello-card-get.json", "id": "65ad94865aed24f70ecdcebb", "badges": { "attachmentsByType": { @@ -12,7 +13,6 @@ "votes": 0, "viewingMemberVoted": false, "subscribed": false, - "lastUpdatedByAi": false, "fogbugz": "", "checkItems": 0, "checkItemsChecked": 0, @@ -22,12 +22,13 @@ "description": true, "due": null, "dueComplete": false, - "start": null + "start": null, + "lastUpdatedByAi": false }, "checkItemStates": [], "closed": false, "dueComplete": false, - "dateLastActivity": "2024-01-21T22:02:47.582Z", + "dateLastActivity": "2024-12-25T08:40:39.500Z", "desc": "A list of the things we think we want to do, maybe not quite ready for work, but high likelihood of being worked on.\n\nThis is the staging area where specs should get fleshed out.\n\nNo limit on the list size, but we should reconsider if it gets long.", "descData": null, "due": null, @@ -40,17 +41,29 @@ "idMembersVoted": [], "idShort": 1, "idAttachmentCover": "65ad94875aed24f70ecdd037", - "labels": [], - "idLabels": [], + "labels": [ + { + "id": "65ad94865aed24f70ecdcee2", + "idBoard": "65ad94865aed24f70ecdce4b", + "idOrganization": "60ae034415aa230ab2ef596d", + "name": "Green", + "nodeId": "ari:cloud:trello::label/workspace/60ae034415aa230ab2ef596d/65ad94865aed24f70ecdcee2", + "color": "green", + "uses": 1 + } + ], + "idLabels": [ + "65ad94865aed24f70ecdcee2" + ], "manualCoverAttachment": false, - "name": "Backlog", + "name": "Task - with description, label and attachment", "pinned": false, "pos": 16384, "shortLink": "Z7CTyW2I", "shortUrl": "https://trello.com/c/Z7CTyW2I", "start": null, "subscribed": false, - "url": "https://trello.com/c/Z7CTyW2I/1-backlog", + "url": "https://trello.com/c/Z7CTyW2I/1-task-with-description-label-and-attachment", "cover": { "idAttachment": "65ad94875aed24f70ecdd037", "color": null, diff --git a/src/tests/responses/trello-list-get-no-desc-or-label.json b/src/tests/responses/trello-list-get-no-desc-or-label.json new file mode 100644 index 0000000..8e94ec0 --- /dev/null +++ b/src/tests/responses/trello-list-get-no-desc-or-label.json @@ -0,0 +1,67 @@ +[ + { + "foo": "trello-list-get-no-desc-or-label.json", + "id": "65ad94865aed24f70ecdcebb", + "badges": { + "attachmentsByType": { + "trello": { + "board": 0, + "card": 0 + } + }, + "externalSource": null, + "location": false, + "votes": 0, + "viewingMemberVoted": false, + "subscribed": false, + "fogbugz": "", + "checkItems": 0, + "checkItemsChecked": 0, + "checkItemsEarliestDue": null, + "comments": 0, + "attachments": 1, + "description": true, + "due": null, + "dueComplete": false, + "start": null, + "lastUpdatedByAi": false + }, + "checkItemStates": [], + "closed": false, + "dueComplete": false, + "dateLastActivity": "2024-01-21T22:02:47.582Z", + "desc": "", + "descData": null, + "due": null, + "dueReminder": null, + "email": null, + "idBoard": "65ad94865aed24f70ecdce4b", + "idChecklists": [], + "idList": "65ad94865aed24f70ecdce4c", + "idMembers": [], + "idMembersVoted": [], + "idShort": 1, + "idAttachmentCover": "65ad94875aed24f70ecdd037", + "labels": [], + "idLabels": [], + "manualCoverAttachment": false, + "name": "Backlog", + "pinned": false, + "pos": 16384, + "shortLink": "Z7CTyW2I", + "shortUrl": "https://trello.com/c/Z7CTyW2I", + "start": null, + "subscribed": false, + "url": "https://trello.com/c/Z7CTyW2I/1-backlog", + "cover": { + "idAttachment": "65ad94875aed24f70ecdd037", + "color": null, + "idUploadedBackground": null, + "size": "normal", + "brightness": "light", + "idPlugin": null + }, + "isTemplate": false, + "cardRole": null + } +] diff --git a/src/tests/responses/trello-list-get.json b/src/tests/responses/trello-list-get.json index 308d3d1..159432d 100644 --- a/src/tests/responses/trello-list-get.json +++ b/src/tests/responses/trello-list-get.json @@ -1,5 +1,6 @@ [ { + "foo": "trello-list-get.json", "id": "65ad94865aed24f70ecdcebb", "badges": { "attachmentsByType": { @@ -41,7 +42,17 @@ "idMembersVoted": [], "idShort": 1, "idAttachmentCover": "65ad94875aed24f70ecdd037", - "labels": [], + "labels": [ + { + "id": "65ad94865aed24f70ecdcee2", + "idBoard": "65ad94865aed24f70ecdce4b", + "idOrganization": "60ae034415aa230ab2ef596d", + "name": "Green", + "nodeId": "ari:cloud:trello::label/workspace/60ae034415aa230ab2ef596d/65ad94865aed24f70ecdcee2", + "color": "green", + "uses": 1 + } + ], "idLabels": [], "manualCoverAttachment": false, "name": "Backlog", @@ -62,69 +73,5 @@ }, "isTemplate": false, "cardRole": null - }, - { - "id": "65ad94865aed24f70ecdced1", - "badges": { - "attachmentsByType": { - "trello": { - "board": 0, - "card": 0 - } - }, - "externalSource": null, - "location": false, - "votes": 0, - "viewingMemberVoted": false, - "subscribed": false, - "fogbugz": "", - "checkItems": 0, - "checkItemsChecked": 0, - "checkItemsEarliestDue": null, - "comments": 0, - "attachments": 0, - "description": false, - "due": null, - "dueComplete": false, - "start": null, - "lastUpdatedByAi": false - }, - "checkItemStates": [], - "closed": false, - "dueComplete": false, - "dateLastActivity": "2024-01-21T22:02:47.112Z", - "desc": "", - "descData": null, - "due": null, - "dueReminder": null, - "email": null, - "idBoard": "65ad94865aed24f70ecdce4b", - "idChecklists": [], - "idList": "65ad94865aed24f70ecdce4c", - "idMembers": [], - "idMembersVoted": [], - "idShort": 12, - "idAttachmentCover": null, - "labels": [], - "idLabels": [], - "manualCoverAttachment": false, - "name": "[Example task]", - "pinned": false, - "pos": 81920, - "shortLink": "kqHXwwev", - "shortUrl": "https://trello.com/c/kqHXwwev", - "start": null, - "subscribed": false, - "url": "https://trello.com/c/kqHXwwev/12-example-task", - "cover": { - "idAttachment": null, - "color": null, - "idUploadedBackground": null, - "size": "normal", - "brightness": "light", - "idPlugin": null - }, - "isTemplate": false, - "cardRole": null } ] diff --git a/src/tests/responses/trello-member-get.json b/src/tests/responses/trello-member-get.json index 15a4e73..92c3e98 100644 --- a/src/tests/responses/trello-member-get.json +++ b/src/tests/responses/trello-member-get.json @@ -1,5 +1,6 @@ [ { + "foo": "trello-member-get.json", "id": "5db72d5517a6135e166fd862", "nodeId": "ari:cloud:trello::board/workspace/60ae020570a89b46695aae66/5db72d5517a6135e166fd862", "name": "0 Business: Cossmass Infinities", diff --git a/src/tests/responses/trello-stack-get.json b/src/tests/responses/trello-stack-get.json index 071975f..5f04a34 100644 --- a/src/tests/responses/trello-stack-get.json +++ b/src/tests/responses/trello-stack-get.json @@ -1,5 +1,6 @@ [ { + "foo": "trello-stack-get.json", "id": "674e03794ef7340ece3ebb5f", "badges": { "attachmentsByType": { diff --git a/src/tests/responses/trello-stack-list.json b/src/tests/responses/trello-stack-list.json index 526d669..ffa0f4b 100644 --- a/src/tests/responses/trello-stack-list.json +++ b/src/tests/responses/trello-stack-list.json @@ -1,5 +1,6 @@ [ { + "foo": "trello-stack-list.json", "id": "65ad94865aed24f70ecdce4c", "name": "Backlog", "closed": false,