commit c97b8974ed50e1759a9594a0913f1888845d3c58
parent c4bd463cef9a40f2f284250ccc4a72099f4c493a
Author: triesap <tyson@radroots.org>
Date: Wed, 3 Jun 2026 18:30:05 -0700
tests: refresh workflow surface coverage
Diffstat:
2 files changed, 248 insertions(+), 1 deletion(-)
diff --git a/crates/desktop/src/window.rs b/crates/desktop/src/window.rs
@@ -13368,6 +13368,8 @@ mod tests {
startup_issue_summary_text, startup_notice_text, startup_signer_preview_summary,
startup_signer_preview_summary_for_connect_state, startup_signer_source_input_is_editable,
startup_signer_status_spec, startup_signer_transport_failure_requires_notice,
+ trade_agreement_status_key, trade_fulfillment_status_key, trade_inventory_status_key,
+ trade_payment_display_status_key, trade_revision_status_key, trade_workflow_source_key,
};
use crate::runtime::{
DesktopAppRuntimeMetadataSummary, DesktopAppRuntimeSummary, DesktopAppSyncConflictSummary,
@@ -13404,7 +13406,9 @@ mod tests {
ReminderDeadlineProjection, ReminderDeliveryState, ReminderId, ReminderKind,
ReminderSurface, ReminderUrgency, RepeatDemandEligibility, RepeatDemandHandoffProjection,
ShellSection, TodayAgendaProjection, TodaySetupTask, TodaySetupTaskKind,
- TradeEconomicsProjection, TradePaymentDisplayStatus, TradeWorkflowProjection,
+ TradeAgreementStatus, TradeEconomicsProjection, TradeFulfillmentStatus,
+ TradeInventoryStatus, TradePaymentDisplayStatus, TradeRevisionStatus,
+ TradeWorkflowProjection, TradeWorkflowSource,
};
use radroots_identity::RadrootsIdentity;
use std::{
@@ -13703,6 +13707,157 @@ mod tests {
}
#[test]
+ fn trade_workflow_badge_keys_cover_refactored_status_axes() {
+ for (status, key) in [
+ (
+ TradeAgreementStatus::Ordered,
+ AppTextKey::TradeWorkflowAgreementOrdered,
+ ),
+ (
+ TradeAgreementStatus::Confirmed,
+ AppTextKey::TradeWorkflowAgreementConfirmed,
+ ),
+ (
+ TradeAgreementStatus::Declined,
+ AppTextKey::TradeWorkflowAgreementDeclined,
+ ),
+ (
+ TradeAgreementStatus::Cancelled,
+ AppTextKey::TradeWorkflowAgreementCancelled,
+ ),
+ (
+ TradeAgreementStatus::Completed,
+ AppTextKey::TradeWorkflowAgreementCompleted,
+ ),
+ (
+ TradeAgreementStatus::NeedsReview,
+ AppTextKey::TradeWorkflowAgreementNeedsReview,
+ ),
+ ] {
+ assert_eq!(trade_agreement_status_key(status), key);
+ assert!(!app_text(key).is_empty());
+ }
+
+ for (status, key) in [
+ (
+ TradeRevisionStatus::None,
+ AppTextKey::TradeWorkflowRevisionNone,
+ ),
+ (
+ TradeRevisionStatus::ChangeProposed,
+ AppTextKey::TradeWorkflowRevisionChangeProposed,
+ ),
+ (
+ TradeRevisionStatus::Updated,
+ AppTextKey::TradeWorkflowRevisionUpdated,
+ ),
+ (
+ TradeRevisionStatus::KeptAsPlaced,
+ AppTextKey::TradeWorkflowRevisionKeptAsPlaced,
+ ),
+ ] {
+ assert_eq!(trade_revision_status_key(status), key);
+ assert!(!app_text(key).is_empty());
+ }
+
+ for (status, key) in [
+ (
+ TradeFulfillmentStatus::Confirmed,
+ AppTextKey::TradeWorkflowFulfillmentConfirmed,
+ ),
+ (
+ TradeFulfillmentStatus::Preparing,
+ AppTextKey::TradeWorkflowFulfillmentPreparing,
+ ),
+ (
+ TradeFulfillmentStatus::ReadyForPickup,
+ AppTextKey::TradeWorkflowFulfillmentReadyForPickup,
+ ),
+ (
+ TradeFulfillmentStatus::OutForDelivery,
+ AppTextKey::TradeWorkflowFulfillmentOutForDelivery,
+ ),
+ (
+ TradeFulfillmentStatus::Delivered,
+ AppTextKey::TradeWorkflowFulfillmentDelivered,
+ ),
+ (
+ TradeFulfillmentStatus::Cancelled,
+ AppTextKey::TradeWorkflowFulfillmentCancelled,
+ ),
+ ] {
+ assert_eq!(trade_fulfillment_status_key(status), key);
+ assert!(!app_text(key).is_empty());
+ }
+
+ for (status, key) in [
+ (
+ TradeInventoryStatus::Available,
+ AppTextKey::TradeWorkflowInventoryAvailable,
+ ),
+ (
+ TradeInventoryStatus::Reserved,
+ AppTextKey::TradeWorkflowInventoryReserved,
+ ),
+ (
+ TradeInventoryStatus::SoldOut,
+ AppTextKey::TradeWorkflowInventorySoldOut,
+ ),
+ (
+ TradeInventoryStatus::NeedsReview,
+ AppTextKey::TradeWorkflowInventoryNeedsReview,
+ ),
+ ] {
+ assert_eq!(trade_inventory_status_key(status), key);
+ assert!(!app_text(key).is_empty());
+ }
+
+ for (status, key) in [
+ (
+ TradePaymentDisplayStatus::NotRecorded,
+ AppTextKey::TradeWorkflowPaymentNotRecorded,
+ ),
+ (
+ TradePaymentDisplayStatus::Recorded,
+ AppTextKey::TradeWorkflowPaymentRecorded,
+ ),
+ (
+ TradePaymentDisplayStatus::NeedsReview,
+ AppTextKey::TradeWorkflowPaymentNeedsReview,
+ ),
+ ] {
+ assert_eq!(trade_payment_display_status_key(status), key);
+ assert!(!app_text(key).is_empty());
+ }
+
+ for (source, key) in [
+ (
+ TradeWorkflowSource::App,
+ AppTextKey::TradeWorkflowProvenanceApp,
+ ),
+ (
+ TradeWorkflowSource::Cli,
+ AppTextKey::TradeWorkflowProvenanceCli,
+ ),
+ (
+ TradeWorkflowSource::Relay,
+ AppTextKey::TradeWorkflowProvenanceRelay,
+ ),
+ (
+ TradeWorkflowSource::LocalEvents,
+ AppTextKey::TradeWorkflowProvenanceLocalEvents,
+ ),
+ (
+ TradeWorkflowSource::Unknown,
+ AppTextKey::TradeWorkflowProvenanceUnknown,
+ ),
+ ] {
+ assert_eq!(trade_workflow_source_key(source), key);
+ assert!(!app_text(key).is_empty());
+ }
+ }
+
+ #[test]
fn today_route_has_no_setup_onboarding_card() {
assert!(farm_setup_onboarding_card_spec(HomeRoute::Today).is_none());
}
diff --git a/crates/store/src/repo/buyer.rs b/crates/store/src/repo/buyer.rs
@@ -3558,6 +3558,71 @@ mod tests {
}
#[test]
+ fn buyer_order_projections_fail_closed_for_invalid_workflow_snapshot_keys() {
+ let store = AppSqliteStore::open(DatabaseTarget::InMemory).expect("store should open");
+ let connection = store.connection();
+ let repository = AppBuyerRepository::new(connection);
+ let context = BuyerContext::account("acct_buyer");
+ let farm_id = insert_farm(connection, "Willow Farm", "ready");
+ let order_id = OrderId::new();
+
+ insert_order(
+ connection,
+ order_id,
+ farm_id,
+ "R-100",
+ "scheduled",
+ Some("account:acct_buyer"),
+ "buyer@example.com",
+ "",
+ "",
+ );
+ set_order_workflow_display_projection(
+ connection,
+ order_id,
+ "confirmed",
+ Some("ready_for_pickup"),
+ "reserved",
+ "recorded",
+ "local_events",
+ Some("buyer-workflow-event"),
+ );
+
+ for (column, expected_field) in [
+ ("workflow_agreement", "orders.workflow_agreement"),
+ ("workflow_fulfillment", "orders.workflow_fulfillment"),
+ ("workflow_inventory", "orders.workflow_inventory"),
+ ("workflow_payment", "orders.workflow_payment"),
+ (
+ "workflow_provenance_source",
+ "orders.workflow_provenance_source",
+ ),
+ ] {
+ set_order_workflow_display_projection(
+ connection,
+ order_id,
+ "confirmed",
+ Some("ready_for_pickup"),
+ "reserved",
+ "recorded",
+ "local_events",
+ Some("buyer-workflow-event"),
+ );
+ corrupt_order_workflow_display_projection(connection, order_id, column, "future_state");
+
+ let list_error = repository
+ .load_buyer_orders(&context)
+ .expect_err("invalid workflow snapshot should fail buyer list projection");
+ let detail_error = repository
+ .load_buyer_order_detail(&context, order_id)
+ .expect_err("invalid workflow snapshot should fail buyer detail projection");
+
+ assert_decode_enum(list_error, expected_field, "future_state");
+ assert_decode_enum(detail_error, expected_field, "future_state");
+ }
+ }
+
+ #[test]
fn buyer_cart_rejects_cross_farm_lines() {
let store = AppSqliteStore::open(DatabaseTarget::InMemory).expect("store should open");
let farm_id = FarmId::new();
@@ -3907,6 +3972,33 @@ mod tests {
.expect("check constraints should re-enable");
}
+ fn corrupt_order_workflow_display_projection(
+ connection: &Connection,
+ order_id: OrderId,
+ column: &str,
+ value: &str,
+ ) {
+ connection
+ .execute_batch("pragma ignore_check_constraints = on")
+ .expect("check constraints should disable");
+ let statement = match column {
+ "workflow_agreement" => "update orders set workflow_agreement = ?1 where id = ?2",
+ "workflow_fulfillment" => "update orders set workflow_fulfillment = ?1 where id = ?2",
+ "workflow_inventory" => "update orders set workflow_inventory = ?1 where id = ?2",
+ "workflow_payment" => "update orders set workflow_payment = ?1 where id = ?2",
+ "workflow_provenance_source" => {
+ "update orders set workflow_provenance_source = ?1 where id = ?2"
+ }
+ _ => panic!("unsupported workflow display projection column {column}"),
+ };
+ connection
+ .execute(statement, params![value, order_id.to_string()])
+ .expect("order workflow display projection corruption should succeed");
+ connection
+ .execute_batch("pragma ignore_check_constraints = off")
+ .expect("check constraints should re-enable");
+ }
+
fn assert_decode_enum(error: AppSqliteError, expected_field: &str, expected_value: &str) {
match error {
AppSqliteError::DecodeEnum { field, value } => {