1use std::collections::{HashMap, HashSet};
10use std::sync::atomic::{AtomicBool, Ordering};
11use std::sync::{Arc, Condvar, Mutex};
12use std::time::{Duration, SystemTime};
13
14use dex_protocol::dex::{
15 AttributeWrite, ChannelInfo, ChannelMessage, ConditionResults, ConditionStatus,
16 Context as ProtoContext, Kv, StepStreamWrite, Value as ProtoValue, value,
17};
18
19use crate::persistence::PersistenceKind;
20use crate::registry::{RegisteredFlow, decode_instance, physical_name};
21use crate::value_mapper;
22use crate::worker_output::StepOutputEmitter;
23use crate::{
24 Attribute, AttributeMap, Channel, ChannelMap, FlowResult, HandlerError, HandlerResult, Stream,
25 Value,
26};
27
28#[derive(Clone, Copy, Eq, PartialEq)]
29pub(crate) enum InvocationMethod {
30 WaitFor,
31 Execute,
32 Rpc,
33}
34
35pub(crate) struct ContextInput {
36 pub(crate) method: InvocationMethod,
37 pub(crate) flow: RegisteredFlow,
38 pub(crate) metadata: ProtoContext,
39 pub(crate) attributes: Vec<Kv>,
40 pub(crate) locals: Vec<Kv>,
41 pub(crate) condition_results: Option<ConditionResults>,
42 pub(crate) channel_infos: HashMap<String, ChannelInfo>,
43}
44
45pub struct Context {
51 method: InvocationMethod,
52 flow: RegisteredFlow,
53 output_emitter: Option<StepOutputEmitter>,
54 metadata: ProtoContext,
55 attributes: HashMap<String, ProtoValue>,
56 locals: HashMap<String, ProtoValue>,
57 condition_results: Option<ConditionResults>,
58 channel_infos: HashMap<String, ChannelInfo>,
59 attribute_writes: HashMap<String, AttributeWrite>,
60 local_writes: HashMap<String, Kv>,
61 events: Vec<Kv>,
62 event_names: HashSet<String>,
63 publications: Vec<ChannelMessage>,
64 cancellation: InvocationCancellation,
65}
66
67impl Context {
68 pub(crate) fn new(
69 input: ContextInput,
70 output_emitter: Option<StepOutputEmitter>,
71 cancellation: InvocationCancellation,
72 ) -> HandlerResult<Self> {
73 Ok(Self {
74 method: input.method,
75 flow: input.flow,
76 output_emitter,
77 metadata: input.metadata,
78 attributes: map_values("Attribute", input.attributes)?,
79 locals: map_values("step-execution local", input.locals)?,
80 condition_results: input.condition_results,
81 channel_infos: input.channel_infos,
82 attribute_writes: HashMap::new(),
83 local_writes: HashMap::new(),
84 events: Vec::new(),
85 event_names: HashSet::new(),
86 publications: Vec::new(),
87 cancellation,
88 })
89 }
90
91 pub fn flow_id(&self) -> &str {
93 &self.metadata.flow_id
94 }
95
96 pub fn run_id(&self) -> &str {
98 &self.metadata.run_id
99 }
100
101 pub fn flow_started_at(&self) -> SystemTime {
103 system_time(self.metadata.flow_started_timestamp)
104 }
105
106 pub fn step_execution_id(&self) -> &str {
108 &self.metadata.step_execution_id
109 }
110
111 pub fn from_step_execution_id(&self) -> &str {
113 &self.metadata.from_step_execution_id
114 }
115
116 pub fn first_attempt_at(&self) -> SystemTime {
118 system_time(self.metadata.first_attempt_timestamp)
119 }
120
121 pub fn attempt(&self) -> u32 {
123 u32::try_from(self.metadata.attempt).unwrap_or_default()
124 }
125
126 pub fn record_heartbeat(&mut self) -> HandlerResult<()> {
136 self.step_output_emitter()?.send_heartbeat(None)
137 }
138
139 pub fn record_heartbeat_value<T: Value>(&mut self, value: T) -> HandlerResult<()> {
168 let value = value_mapper::encode_handler(&value)?;
169 self.step_output_emitter()?.send_heartbeat(Some(value))
170 }
171
172 pub fn last_heartbeat_value<T: Value>(&self) -> HandlerResult<Option<T>> {
183 if self.method == InvocationMethod::Rpc {
184 return Err(HandlerError::new(
185 "dex_sdk::HandlerError",
186 "Heartbeat values require a Step Context",
187 ));
188 }
189 self.metadata
190 .last_heartbeat_value
191 .as_ref()
192 .map(value_mapper::decode_handler)
193 .transpose()
194 }
195
196 pub fn has_any_timer_fired(&self) -> bool {
198 self.condition_results.as_ref().is_some_and(|results| {
199 results
200 .timer_results
201 .iter()
202 .any(|timer| timer.condition_status == ConditionStatus::Completed as i32)
203 })
204 }
205
206 pub fn has_timer_fired(&self, index: usize) -> bool {
210 self.condition_results
211 .as_ref()
212 .and_then(|results| results.timer_results.get(index))
213 .is_some_and(|timer| timer.condition_status == ConditionStatus::Completed as i32)
214 }
215
216 pub fn wait_for_method_failed(&self) -> bool {
218 self.condition_results
219 .as_ref()
220 .is_some_and(|results| results.wait_for_failed)
221 }
222
223 pub(crate) fn sub_flow_result(&self, index: usize) -> HandlerResult<FlowResult> {
224 if self.method != InvocationMethod::Execute {
225 return Err(HandlerError::new(
226 "dex_sdk::HandlerError",
227 "SubFlow results are available only during execute",
228 ));
229 }
230 let result = self
231 .condition_results
232 .as_ref()
233 .and_then(|results| results.sub_flow_results.get(index))
234 .ok_or_else(|| {
235 HandlerError::new(
236 "dex_sdk::HandlerError",
237 format!("SubFlow result index is unavailable: {index}"),
238 )
239 })?;
240 FlowResult::from_proto(result).map_err(HandlerError::from_error)
241 }
242
243 pub(crate) fn sub_flow_id(&self, index: usize) -> HandlerResult<String> {
244 self.sub_flow_result(index)?;
245 Ok(format!(
246 "SubFlow:{}-{}-{index}",
247 self.flow_id(),
248 self.step_execution_id()
249 ))
250 }
251
252 pub fn wait_for_cancellation(&self) {
256 self.cancellation.wait();
257 }
258
259 pub fn is_cancelled(&self) -> bool {
261 self.cancellation.is_cancelled()
262 }
263
264 pub fn set_step_execution_local<T: Value>(&mut self, key: &str, value: T) -> HandlerResult<()> {
270 require_name(key, "step-execution local key")?;
271 let value = value_mapper::encode_handler(&value)?;
272 self.local_writes.insert(
273 key.to_string(),
274 Kv {
275 key: key.to_string(),
276 value: Some(value),
277 },
278 );
279 Ok(())
280 }
281
282 pub fn step_execution_local<T: Value>(&self, key: &str) -> HandlerResult<T> {
288 require_name(key, "step-execution local key")?;
289 let value = self
290 .local_writes
291 .get(key)
292 .and_then(|entry| entry.value.as_ref())
293 .or_else(|| self.locals.get(key))
294 .ok_or_else(|| {
295 HandlerError::new(
296 "dex_sdk::HandlerError",
297 format!("step-execution local {key} is missing"),
298 )
299 })?;
300 value_mapper::decode_handler(value)
301 }
302
303 pub fn record_event<T: Value>(&mut self, name: &str, value: T) -> HandlerResult<()> {
311 require_name(name, "event name")?;
312 if !self.event_names.insert(name.to_string()) {
313 return Err(HandlerError::new(
314 "dex_sdk::HandlerError",
315 format!("event was already recorded: {name}"),
316 ));
317 }
318 self.events.push(Kv {
319 key: name.to_string(),
320 value: Some(value_mapper::encode_handler(&value)?),
321 });
322 Ok(())
323 }
324
325 pub fn get_attribute<T: Value>(&self, attribute: &Attribute<T>) -> HandlerResult<Option<T>> {
327 self.get_attribute_value(attribute.name(), PersistenceKind::Attribute, None)
328 }
329
330 pub fn get_attribute_map<T: Value>(
332 &self,
333 attribute: &AttributeMap<T>,
334 instance: &str,
335 ) -> HandlerResult<Option<T>> {
336 self.get_attribute_value(
337 attribute.name(),
338 PersistenceKind::AttributeMap,
339 Some(instance),
340 )
341 }
342
343 pub fn set_attribute<T: Value>(
345 &mut self,
346 attribute: &Attribute<T>,
347 value: T,
348 ) -> HandlerResult<()> {
349 self.set_attribute_value(
350 attribute.name(),
351 PersistenceKind::Attribute,
352 None,
353 value_mapper::encode_handler(&value)?,
354 attribute.index().map(|index| index.proto_config(false)),
355 attribute.sync_config(),
356 )
357 }
358
359 pub fn set_attribute_map<T: Value>(
361 &mut self,
362 attribute: &AttributeMap<T>,
363 instance: &str,
364 value: T,
365 ) -> HandlerResult<()> {
366 self.set_attribute_value(
367 attribute.name(),
368 PersistenceKind::AttributeMap,
369 Some(instance),
370 value_mapper::encode_handler(&value)?,
371 attribute.index().map(|index| index.proto_config(true)),
372 attribute.sync_config(),
373 )
374 }
375
376 pub fn delete_attribute<T>(&mut self, attribute: &Attribute<T>) -> HandlerResult<()> {
378 self.set_attribute_value(
379 attribute.name(),
380 PersistenceKind::Attribute,
381 None,
382 value_mapper::deletion(),
383 attribute.index().map(|index| index.proto_config(false)),
384 attribute.sync_config(),
385 )
386 }
387
388 pub fn delete_attribute_map<T>(
390 &mut self,
391 attribute: &AttributeMap<T>,
392 instance: &str,
393 ) -> HandlerResult<()> {
394 self.set_attribute_value(
395 attribute.name(),
396 PersistenceKind::AttributeMap,
397 Some(instance),
398 value_mapper::deletion(),
399 attribute.index().map(|index| index.proto_config(true)),
400 attribute.sync_config(),
401 )
402 }
403
404 pub(crate) fn attribute_map_keys<T>(
405 &self,
406 attribute: &AttributeMap<T>,
407 ) -> HandlerResult<Vec<String>> {
408 self.registered_name(
409 attribute.name(),
410 PersistenceKind::AttributeMap,
411 Some("key-check"),
412 )?;
413 let prefix = format!("{}/", attribute.name());
414 let mut physical_keys = self
415 .attributes
416 .keys()
417 .filter(|key| key.starts_with(&prefix))
418 .cloned()
419 .collect::<HashSet<_>>();
420 for (key, write) in &self.attribute_writes {
421 if !key.starts_with(&prefix) {
422 continue;
423 }
424 if matches!(
425 write.value.as_ref().and_then(|value| value.kind.as_ref()),
426 Some(value::Kind::NullValue(_))
427 ) {
428 physical_keys.remove(key);
429 } else {
430 physical_keys.insert(key.clone());
431 }
432 }
433 sorted_instance_keys(&prefix, physical_keys)
434 }
435
436 pub fn publish<T: Value>(&mut self, channel: &Channel<T>, value: T) -> HandlerResult<()> {
438 self.publish_value(
439 channel.name(),
440 PersistenceKind::Channel,
441 None,
442 value_mapper::encode_handler(&value)?,
443 )
444 }
445
446 pub fn publish_map<T: Value>(
448 &mut self,
449 channel: &ChannelMap<T>,
450 instance: &str,
451 value: T,
452 ) -> HandlerResult<()> {
453 self.publish_value(
454 channel.name(),
455 PersistenceKind::ChannelMap,
456 Some(instance),
457 value_mapper::encode_handler(&value)?,
458 )
459 }
460
461 pub fn channel_size<T>(&self, channel: &Channel<T>) -> HandlerResult<usize> {
463 self.channel_size_value(channel.name(), PersistenceKind::Channel, None)
464 }
465
466 pub fn channel_map_size<T>(
468 &self,
469 channel: &ChannelMap<T>,
470 instance: &str,
471 ) -> HandlerResult<usize> {
472 self.channel_size_value(channel.name(), PersistenceKind::ChannelMap, Some(instance))
473 }
474
475 pub(crate) fn channel_map_keys<T>(
476 &self,
477 channel: &ChannelMap<T>,
478 ) -> HandlerResult<Vec<String>> {
479 if self.method != InvocationMethod::Rpc {
480 return Err(HandlerError::new(
481 "dex_sdk::HandlerError",
482 "ChannelMap introspection requires an RPC invocation",
483 ));
484 }
485 self.registered_name(
486 channel.name(),
487 PersistenceKind::ChannelMap,
488 Some("key-check"),
489 )?;
490 let prefix = format!("{}/", channel.name());
491 let physical_keys = self
492 .channel_infos
493 .iter()
494 .filter(|(key, info)| key.starts_with(&prefix) && info.size > 0)
495 .map(|(key, _)| key.clone())
496 .collect::<HashSet<_>>();
497 sorted_instance_keys(&prefix, physical_keys)
498 }
499
500 pub fn channel_results<T: Value>(&self, channel: &Channel<T>) -> HandlerResult<Vec<T>> {
502 self.channel_results_value(channel.name(), PersistenceKind::Channel, None)
503 }
504
505 pub fn channel_map_results<T: Value>(
507 &self,
508 channel: &ChannelMap<T>,
509 instance: &str,
510 ) -> HandlerResult<Vec<T>> {
511 self.channel_results_value(channel.name(), PersistenceKind::ChannelMap, Some(instance))
512 }
513
514 pub fn write_stream<T: Value>(&mut self, stream: &Stream<T>, value: T) -> HandlerResult<()> {
525 let output_emitter = self.step_output_emitter()?.clone();
526 let definition = self
527 .flow
528 .persistence
529 .get(stream.name())
530 .filter(|definition| {
531 definition.kind == PersistenceKind::Stream
532 && definition.stream_identity == Some(stream.identity())
533 })
534 .ok_or_else(|| {
535 HandlerError::new(
536 "dex_sdk::HandlerError",
537 format!("Stream does not belong to Flow: {}", stream.name()),
538 )
539 })?;
540 if definition.stream_capacity_bytes != Some(stream.stream_capacity_bytes()) {
541 return Err(HandlerError::new(
542 "dex_sdk::HandlerError",
543 format!(
544 "Stream capacity does not match its registered definition: {}",
545 stream.name()
546 ),
547 ));
548 }
549 output_emitter.send_stream_write(StepStreamWrite {
550 stream_name: stream.name().to_string(),
551 stream_capacity_bytes: stream.stream_capacity_bytes(),
552 value: Some(value_mapper::encode_handler(&value)?),
553 })
554 }
555
556 pub(crate) fn cancellation(&self) -> InvocationCancellation {
557 self.cancellation.clone()
558 }
559
560 pub(crate) fn take_outputs(
561 self,
562 ) -> (Vec<AttributeWrite>, Vec<Kv>, Vec<Kv>, Vec<ChannelMessage>) {
563 (
564 self.attribute_writes.into_values().collect(),
565 self.local_writes.into_values().collect(),
566 self.events,
567 self.publications,
568 )
569 }
570
571 fn step_output_emitter(&self) -> HandlerResult<&StepOutputEmitter> {
572 if self.method == InvocationMethod::Rpc {
573 return Err(HandlerError::new(
574 "dex_sdk::HandlerError",
575 "Step progress requires a Step Context",
576 ));
577 }
578 if self.cancellation.is_cancelled() {
579 return Err(HandlerError::new(
580 "dex_sdk::HandlerError",
581 "Step invocation is canceled",
582 ));
583 }
584 self.output_emitter.as_ref().ok_or_else(|| {
585 HandlerError::new("dex_sdk::HandlerError", "Step output stream is unavailable")
586 })
587 }
588
589 fn get_attribute_value<T: Value>(
590 &self,
591 name: &str,
592 kind: PersistenceKind,
593 instance: Option<&str>,
594 ) -> HandlerResult<Option<T>> {
595 let key = self.registered_name(name, kind, instance)?;
596 if let Some(write) = self.attribute_writes.get(&key) {
597 let value = write.value.as_ref().ok_or_else(|| {
598 HandlerError::new(
599 "dex_sdk::HandlerError",
600 format!("Attribute write {key} has no Value"),
601 )
602 })?;
603 if matches!(value.kind, Some(value::Kind::NullValue(_))) {
604 return Ok(None);
605 }
606 return value_mapper::decode_handler(value).map(Some);
607 }
608 self.attributes
609 .get(&key)
610 .map(value_mapper::decode_handler)
611 .transpose()
612 }
613
614 fn set_attribute_value(
615 &mut self,
616 name: &str,
617 kind: PersistenceKind,
618 instance: Option<&str>,
619 value: ProtoValue,
620 index_config: Option<dex_protocol::dex::IndexConfig>,
621 sync_config: Option<dex_protocol::dex::AttributeSyncConfig>,
622 ) -> HandlerResult<()> {
623 let key = self.registered_name(name, kind, instance)?;
624 self.attribute_writes.insert(
625 key.clone(),
626 AttributeWrite {
627 key,
628 value: Some(value),
629 index_config,
630 sync_config,
631 },
632 );
633 Ok(())
634 }
635
636 fn publish_value(
637 &mut self,
638 name: &str,
639 kind: PersistenceKind,
640 instance: Option<&str>,
641 value: ProtoValue,
642 ) -> HandlerResult<()> {
643 let channel_name = self.registered_name(name, kind, instance)?;
644 self.publications.push(ChannelMessage {
645 channel_name: channel_name.clone(),
646 value: Some(value),
647 });
648 if self.method == InvocationMethod::Rpc {
649 self.channel_infos
650 .entry(channel_name)
651 .and_modify(|info| info.size += 1)
652 .or_insert(ChannelInfo { size: 1 });
653 }
654 Ok(())
655 }
656
657 fn channel_size_value(
658 &self,
659 name: &str,
660 kind: PersistenceKind,
661 instance: Option<&str>,
662 ) -> HandlerResult<usize> {
663 let channel_name = self.registered_name(name, kind, instance)?;
664 Ok(self
665 .channel_infos
666 .get(&channel_name)
667 .map_or(0, |info| usize::try_from(info.size).unwrap_or_default()))
668 }
669
670 fn channel_results_value<T: Value>(
671 &self,
672 name: &str,
673 kind: PersistenceKind,
674 instance: Option<&str>,
675 ) -> HandlerResult<Vec<T>> {
676 let channel_name = self.registered_name(name, kind, instance)?;
677 let Some(results) = &self.condition_results else {
678 return Ok(Vec::new());
679 };
680 let mut decoded = Vec::new();
681 for result in &results.channel_results {
682 if result.channel_name == channel_name
683 && result.condition_status == ConditionStatus::Completed as i32
684 {
685 for value in &result.values {
686 decoded.push(value_mapper::decode_handler(value)?);
687 }
688 }
689 }
690 Ok(decoded)
691 }
692
693 fn registered_name(
694 &self,
695 name: &str,
696 kind: PersistenceKind,
697 instance: Option<&str>,
698 ) -> HandlerResult<String> {
699 let definition = self.flow.persistence.get(name).ok_or_else(|| {
700 HandlerError::new(
701 "dex_sdk::HandlerError",
702 format!("persistence definition does not belong to Flow: {name}"),
703 )
704 })?;
705 if definition.kind != kind {
706 return Err(HandlerError::new(
707 "dex_sdk::HandlerError",
708 format!("persistence definition kind does not match: {name}"),
709 ));
710 }
711 match kind {
712 PersistenceKind::AttributeMap | PersistenceKind::ChannelMap => instance
713 .map(|instance| physical_name(name, instance))
714 .ok_or_else(|| {
715 HandlerError::new(
716 "dex_sdk::HandlerError",
717 format!("{name} requires an instance"),
718 )
719 }),
720 PersistenceKind::Attribute | PersistenceKind::Channel if instance.is_none() => {
721 Ok(name.to_string())
722 }
723 _ => Err(HandlerError::new(
724 "dex_sdk::HandlerError",
725 format!("static persistence definition cannot use an instance: {name}"),
726 )),
727 }
728 }
729}
730
731fn sorted_instance_keys(
732 prefix: &str,
733 physical_keys: HashSet<String>,
734) -> HandlerResult<Vec<String>> {
735 let mut keys = physical_keys
736 .iter()
737 .map(|key| {
738 decode_instance(key, prefix)
739 .map_err(|error| HandlerError::new("dex_sdk::HandlerError", error))
740 })
741 .collect::<HandlerResult<Vec<_>>>()?;
742 keys.sort();
743 Ok(keys)
744}
745
746#[derive(Clone)]
747pub(crate) struct InvocationCancellation {
748 inner: Arc<InvocationCancellationInner>,
749}
750
751struct InvocationCancellationInner {
752 cancelled: AtomicBool,
753 mutex: Mutex<()>,
754 condition: Condvar,
755}
756
757impl InvocationCancellation {
758 pub(crate) fn new() -> Self {
759 Self {
760 inner: Arc::new(InvocationCancellationInner {
761 cancelled: AtomicBool::new(false),
762 mutex: Mutex::new(()),
763 condition: Condvar::new(),
764 }),
765 }
766 }
767
768 fn wait(&self) {
769 let mut guard = self.inner.mutex.lock().expect("cancellation lock");
770 while !self.is_cancelled() {
771 guard = self.inner.condition.wait(guard).expect("cancellation lock");
772 }
773 }
774
775 pub(crate) fn is_cancelled(&self) -> bool {
776 self.inner.cancelled.load(Ordering::Acquire)
777 }
778
779 pub(crate) fn cancel(&self) {
780 let _guard = self.inner.mutex.lock().expect("cancellation lock");
781 self.inner.cancelled.store(true, Ordering::Release);
782 self.inner.condition.notify_all();
783 }
784}
785
786fn system_time(timestamp: i64) -> SystemTime {
787 if timestamp >= 0 {
788 SystemTime::UNIX_EPOCH + Duration::from_secs(timestamp as u64)
789 } else {
790 SystemTime::UNIX_EPOCH - Duration::from_secs(timestamp.unsigned_abs())
791 }
792}
793
794fn map_values(kind: &str, entries: Vec<Kv>) -> HandlerResult<HashMap<String, ProtoValue>> {
795 let mut mapped = HashMap::new();
796 for entry in entries {
797 require_name(&entry.key, kind)?;
798 let value = entry.value.ok_or_else(|| {
799 HandlerError::new(
800 "dex_sdk::HandlerError",
801 format!("{kind} {} has no Value", entry.key),
802 )
803 })?;
804 if mapped.insert(entry.key.clone(), value).is_some() {
805 return Err(HandlerError::new(
806 "dex_sdk::HandlerError",
807 format!("duplicate {kind} {}", entry.key),
808 ));
809 }
810 }
811 Ok(mapped)
812}
813
814fn require_name(value: &str, kind: &str) -> HandlerResult<()> {
815 if value.is_empty() {
816 Err(HandlerError::new(
817 "dex_sdk::HandlerError",
818 format!("{kind} is required"),
819 ))
820 } else {
821 Ok(())
822 }
823}
824
825#[cfg(test)]
826mod tests {
827 use super::{Context, ContextInput, InvocationCancellation, InvocationMethod};
828 use crate::{
829 AttributeMap, ChannelMap, Flow, PersistenceSchema, Registry, registry::physical_name,
830 value_mapper,
831 };
832 use dex_protocol::dex::{ChannelInfo, Context as ProtoContext, Kv};
833 use std::collections::HashMap;
834
835 struct MapFlow {
836 attributes: AttributeMap<String>,
837 channels: ChannelMap<String>,
838 }
839
840 impl Flow for MapFlow {
841 type StartInput = ();
842
843 fn persistence(&self) -> PersistenceSchema {
844 PersistenceSchema::new()
845 .attribute_map(&self.attributes)
846 .channel_map(&self.channels)
847 }
848 }
849
850 #[test]
851 fn map_introspection_tracks_buffered_changes() {
852 let flow = MapFlow {
853 attributes: AttributeMap::new("items"),
854 channels: ChannelMap::new("messages"),
855 };
856 let registry = Registry::new().register(flow).expect("register map Flow");
857 let registered = registry.flow("MapFlow").expect("lookup map Flow").clone();
858 let attributes = AttributeMap::<String>::new("items");
859 let channels = ChannelMap::<String>::new("messages");
860 let special = "special / key";
861 let mut context = Context::new(
862 ContextInput {
863 method: InvocationMethod::Rpc,
864 flow: registered,
865 metadata: ProtoContext::default(),
866 attributes: vec![
867 Kv {
868 key: physical_name("items", special),
869 value: Some(value_mapper::encode_handler(&"initial".to_string()).unwrap()),
870 },
871 Kv {
872 key: physical_name("items", "z"),
873 value: Some(value_mapper::encode_handler(&"remove".to_string()).unwrap()),
874 },
875 ],
876 locals: Vec::new(),
877 condition_results: None,
878 channel_infos: HashMap::from([
879 (physical_name("messages", special), ChannelInfo { size: 1 }),
880 (physical_name("messages", "empty"), ChannelInfo { size: 0 }),
881 ]),
882 },
883 None,
884 InvocationCancellation::new(),
885 )
886 .expect("create RPC Context");
887
888 assert_eq!(
889 vec![special.to_string(), "z".to_string()],
890 attributes.all_instance_keys(&context).unwrap()
891 );
892 attributes
893 .set(&mut context, "a", "added".to_string())
894 .unwrap();
895 attributes.delete(&mut context, "z").unwrap();
896 assert_eq!(
897 vec!["a".to_string(), special.to_string()],
898 attributes.all_instance_keys(&context).unwrap()
899 );
900 assert_eq!(2, attributes.map_size(&context).unwrap());
901
902 assert_eq!(
903 vec![special.to_string()],
904 channels.all_instance_keys(&context).unwrap()
905 );
906 channels
907 .publish(&mut context, "a", "published".to_string())
908 .unwrap();
909 assert_eq!(
910 vec!["a".to_string(), special.to_string()],
911 channels.all_instance_keys(&context).unwrap()
912 );
913 assert_eq!(2, channels.map_size(&context).unwrap());
914 assert!(context.record_heartbeat().is_err());
915 assert!(
916 context
917 .record_heartbeat_value("checkpoint".to_string())
918 .is_err()
919 );
920 assert!(context.last_heartbeat_value::<String>().is_err());
921 }
922}