ruddr 1.1.1

A lightweight Ruddr SDK Rust crate
Documentation
use super::*;

#[test]
fn test_project_deserialize() {
    let json_input = r#"
        {
          "id": "095e0780-48bf-472c-8deb-2fc3ebc7d90c",
          "key": "vendor-portal",
          "name": "Vendor Portal",
          "notes": "The client would like to develop a mobile app that rewards its customers for repeat purchases.",
          "statusId": "in_progress",
          "start": "2021-09-01",
          "end": "2022-01-31",
          "code": "P-2021-00068",
          "poNumber": "DM-2021-02059",
          "billingTypeId": "fixed_recurring",
          "isBillable": true,
          "currency": "USD",
          "revenueRecognitionMethod": "manual",
          "fixedFee": null,
          "fixedRecurringFee": null,
          "fixedRecurringStart": null,
          "fixedRecurringEnd": null,
          "useRoles": true,
          "useBudget": true,
          "budgetMode": "detailed",
          "useMonthlyBudget": true,
          "monthlyBudgetMode": "detailed",
          "capMaxMemberHoursPerDay": false,
          "maxMemberHoursPerDay": null,
          "capMaxMemberHoursPerWeek": false,
          "maxMemberHoursPerWeek": null,
          "capMaxMemberHoursPerMonth": false,
          "maxMemberHoursPerMonth": null,
          "requiresNotes": true,
          "requiresTasks": true,
          "recordStatusId": "active",
          "isProductive": null,
          "lockTimeAndExpenses": false,
          "trackTimeToAssignedRoles": true,
          "cloudFolderUrl": "https://drive.google.com/drive/folders/4MCmjW",
          "createdAt": "2022-03-15T14:59:18.825Z",
          "completedOn": "2025-10-28",
          "client": {
            "id": "d5afaffe-09e5-4d73-b02c-905b40fc6c22",
            "name": "Acme Company"
          },
          "practice": {
            "id": "40f95471-7f7c-4ffa-b838-8dcccab0f54a",
            "name": "Digital Transformation"
          },
          "projectType": {
            "id": "9b0927a6-35a1-4795-a4ca-10167b05f7de",
            "name": "Content Management"
          },
          "tags": [
            {
              "id": "626db436-98bf-40cb-9937-c382af5d818c",
              "name": "Atlanta Office"
            },
            {
              "id": "9f26fb15-23f1-49a6-8558-c19ad4338472",
              "name": "Data Analytics"
            }
          ],
          "salesRepresentative": {
            "id": "a6816355-8945-40aa-b798-b0d6fd89e437",
            "name": "Joe"
          },
          "businessUnit": {
            "id": "0e8351ea-6b3c-4307-97cc-196448de0ef1",
            "name": "Palermo North America"
          },
          "projectGroup": {
            "id": "c93c5988-e8d8-4e14-acb0-a991f1ef7c4c",
            "name": "North America Group"
          },
          "budget": {
            "revenue": 602500,
            "servicesRevenue": 600000,
            "otherRevenue": 2000,
            "billableExpenses": 500,
            "nonBillableExpenses": 150,
            "billableHours": 150,
            "nonBillableHours": 10
          },
          "monthlyBudget": {
            "revenue": 51220,
            "servicesRevenue": 50000,
            "otherRevenue": 920,
            "billableExpenses": 300,
            "nonBillableExpenses": 50,
            "billableHours": 10,
            "nonBillableHours": 2
          }
        }"#;
    let project_deserialized =
        serde_json::from_str::<Project>(json_input).expect("time entry could not be deserialized");
    let project = Project {
        id: types::UUID(String::from("095e0780-48bf-472c-8deb-2fc3ebc7d90c")),
        key: types::Slug(String::from("vendor-portal")),
        name: String::from("Vendor Portal"),
        notes: String::from(
            "The client would like to develop a mobile app that rewards its customers for repeat purchases.",
        ),
        status_id: enums::Status::InProgress,
        start: Some(types::Date(String::from("2021-09-01"))),
        end: Some(types::Date(String::from("2022-01-31"))),
        code: String::from("P-2021-00068"),
        po_number: String::from("DM-2021-02059"),
        billing_type_id: BillingType::FixedRecurring,
        is_billable: true,
        currency: String::from("USD"),
        revenue_recognition_method: Some(RevenueRecognitionMethod::Manual),
        fixed_fee: None,
        fixed_recurring_fee: None,
        fixed_recurring_start: None,
        fixed_recurring_end: None,
        use_roles: true,
        use_budget: true,
        budget_mode: Some(BudgetMode::Detailed),
        use_monthly_budget: true,
        monthly_budget_mode: Some(MonthlyBudgetMode::Detailed),
        cap_max_member_hours_per_day: false,
        max_member_hours_per_day: None,
        cap_max_member_hours_per_week: false,
        max_member_hours_per_week: None,
        cap_max_member_hours_per_month: false,
        max_member_hours_per_month: None,
        requires_notes: true,
        requires_tasks: true,
        record_status_id: RecordStatus::Active,
        is_productive: None,
        lock_time_and_expenses: false,
        track_time_to_assigned_roles: true,
        cloud_folder_url: String::from("https://drive.google.com/drive/folders/4MCmjW"),
        created_at: types::Timestamp(String::from("2022-03-15T14:59:18.825Z")),
        completed_on: Some(types::Date(String::from("2025-10-28"))),
        client: shared::Entity {
            id: types::UUID(String::from("d5afaffe-09e5-4d73-b02c-905b40fc6c22")),
            name: String::from("Acme Company"),
        },
        practice: shared::Entity {
            id: types::UUID(String::from("40f95471-7f7c-4ffa-b838-8dcccab0f54a")),
            name: String::from("Digital Transformation"),
        },
        project_type: shared::Entity {
            id: types::UUID(String::from("9b0927a6-35a1-4795-a4ca-10167b05f7de")),
            name: String::from("Content Management"),
        },
        tags: vec![
            shared::Entity {
                id: types::UUID(String::from("626db436-98bf-40cb-9937-c382af5d818c")),
                name: String::from("Atlanta Office"),
            },
            shared::Entity {
                id: types::UUID(String::from("9f26fb15-23f1-49a6-8558-c19ad4338472")),
                name: String::from("Data Analytics"),
            },
        ],
        sales_representative: Some(shared::Entity {
            id: types::UUID(String::from("a6816355-8945-40aa-b798-b0d6fd89e437")),
            name: String::from("Joe"),
        }),
        business_unit: Some(shared::Entity {
            id: types::UUID(String::from("0e8351ea-6b3c-4307-97cc-196448de0ef1")),
            name: String::from("Palermo North America"),
        }),
        project_group: Some(shared::Entity {
            id: types::UUID(String::from("c93c5988-e8d8-4e14-acb0-a991f1ef7c4c")),
            name: String::from("North America Group"),
        }),
        budget: Some(Budget {
            revenue: 602500,
            services_revenue: 600000,
            other_revenue: 2000,
            billable_expenses: 500,
            non_billable_expenses: 150,
            billable_hours: 150,
            non_billable_hours: 10,
        }),
        monthly_budget: Some(MonthlyBudget {
            revenue: 51220,
            services_revenue: 50000,
            other_revenue: 920,
            billable_expenses: 300,
            non_billable_expenses: 50,
            billable_hours: 10,
            non_billable_hours: 2,
        }),
    };
    assert_eq!(
        project_deserialized, project,
        "time entry did not contain the expected values"
    );

    let json_inputs = &format!(
        r#"
        {{"results": [{json_input}],"hasMore": false}}
        "#
    );
    let projects_deserialized =
        serde_json::from_str::<Projects>(json_inputs).expect("projects could not be deserialized");
    let projects = Projects {
        results: vec![project],
        has_more: false,
    };
    assert_eq!(
        projects_deserialized, projects,
        "projects did not contain the expected values"
    );
}