LCOV - code coverage report
Current view: top level - antares-model/src - operations.rs (source / functions) Coverage Total Hit
Test: merged.info Lines: 100.0 % 51 51
Test Date: 2026-09-21 10:31:06 Functions: 87.5 % 8 7

            Line data    Source code
       1              : // SPDX-License-Identifier: EUPL-1.2
       2              : //! Distributed operation names (CIM 009 clause 4.20).
       3              : //!
       4              : //! Table 4.20-1 names every API operation a Context Source may declare in the
       5              : //! 5.2.9 `operations` member; Table 4.20-2 names five groups that each stand
       6              : //! for a set of them. The vocabulary lives here because two independent
       7              : //! places read it — the registration validator that decides which names are
       8              : //! accepted, and the match index that decides which registrations a
       9              : //! distributed operation reaches. A name present in one and absent from the
      10              : //! other is a registration the API takes and the broker never forwards to.
      11              : 
      12              : /// Table 4.20-1: every named API operation, in the table's own order.
      13              : ///
      14              : /// The order is APPEND-ONLY. A backend may store a set of these as a bitmask
      15              : /// whose bit position is the index (the Postgres `csource_index.ops` column
      16              : /// does), so reordering or removing a name rewrites the meaning of rows
      17              : /// already written: it is a migration, never an edit.
      18              : pub const OPERATION_NAMES: &[&str] = &[
      19              :     "createEntity",
      20              :     "updateEntity",
      21              :     "appendAttrs",
      22              :     "updateAttrs",
      23              :     "deleteAttrs",
      24              :     "deleteEntity",
      25              :     "createBatch",
      26              :     "upsertBatch",
      27              :     "updateBatch",
      28              :     "deleteBatch",
      29              :     "upsertTemporal",
      30              :     "appendAttrsTemporal",
      31              :     "deleteAttrsTemporal",
      32              :     "updateAttrInstanceTemporal",
      33              :     "deleteAttrInstanceTemporal",
      34              :     "deleteTemporal",
      35              :     "mergeEntity",
      36              :     "replaceEntity",
      37              :     "replaceAttrs",
      38              :     "mergeBatch",
      39              :     "purgeEntity",
      40              :     "retrieveEntity",
      41              :     "queryEntity",
      42              :     "queryBatch",
      43              :     "retrieveTemporal",
      44              :     "queryTemporal",
      45              :     "retrieveEntityTypes",
      46              :     "retrieveEntityTypeDetails",
      47              :     "retrieveEntityTypeInfo",
      48              :     "retrieveAttrTypes",
      49              :     "retrieveAttrTypeDetails",
      50              :     "retrieveAttrTypeInfo",
      51              :     "createSubscription",
      52              :     "updateSubscription",
      53              :     "retrieveSubscription",
      54              :     "querySubscription",
      55              :     "deleteSubscription",
      56              :     "retrieveEntityMap",
      57              :     "updateEntityMap",
      58              :     "deleteEntityMap",
      59              :     "createEntityMapQueryEntity",
      60              :     "createEntityMapQueryTemporal",
      61              :     "retrieveContextSourceIdentity",
      62              : ];
      63              : 
      64              : /// Table 4.20-2: the five group names. They are legal `operations` values
      65              : /// alongside the individual names of Table 4.20-1 and are expanded to their
      66              : /// members before an operation is matched against a registration.
      67              : pub const OPERATION_GROUPS: &[&str] = &[
      68              :     "federationOps",
      69              :     "associationOps",
      70              :     "updateOps",
      71              :     "retrieveOps",
      72              :     "redirectionOps",
      73              : ];
      74              : 
      75              : /// 4.20: "If no specific subset of operations is defined for a Context Source
      76              : /// Registration, the default set of operations matches the group defined as
      77              : /// federationOps."
      78              : pub const DEFAULT_OPERATION_GROUP: &str = "federationOps";
      79              : 
      80              : /// The Table 4.20-2 members of one group name, or `None` when the name is not
      81              : /// a group (an individual operation name, or a name outside the vocabulary).
      82        80783 : pub fn group_members(name: &str) -> Option<&'static [&'static str]> {
      83              :     /// federationOps: the consumption and subscription operations plus the
      84              :     /// EntityMap support operations, minus createEntityMapQueryTemporal.
      85              :     const FEDERATION: &[&str] = &[
      86              :         "retrieveEntity",
      87              :         "queryEntity",
      88              :         "queryBatch",
      89              :         "retrieveEntityTypes",
      90              :         "retrieveEntityTypeDetails",
      91              :         "retrieveEntityTypeInfo",
      92              :         "retrieveAttrTypes",
      93              :         "retrieveAttrTypeDetails",
      94              :         "retrieveAttrTypeInfo",
      95              :         "createSubscription",
      96              :         "updateSubscription",
      97              :         "retrieveSubscription",
      98              :         "querySubscription",
      99              :         "deleteSubscription",
     100              :         "retrieveEntityMap",
     101              :         "updateEntityMap",
     102              :         "deleteEntityMap",
     103              :         "createEntityMapQueryEntity",
     104              :         "retrieveContextSourceIdentity",
     105              :     ];
     106              :     /// associationOps: federationOps WITHOUT the EntityMap support operations.
     107              :     const ASSOCIATION: &[&str] = &[
     108              :         "retrieveEntity",
     109              :         "queryEntity",
     110              :         "queryBatch",
     111              :         "retrieveEntityTypes",
     112              :         "retrieveEntityTypeDetails",
     113              :         "retrieveEntityTypeInfo",
     114              :         "retrieveAttrTypes",
     115              :         "retrieveAttrTypeDetails",
     116              :         "retrieveAttrTypeInfo",
     117              :         "createSubscription",
     118              :         "updateSubscription",
     119              :         "retrieveSubscription",
     120              :         "querySubscription",
     121              :         "deleteSubscription",
     122              :         "retrieveContextSourceIdentity",
     123              :     ];
     124              :     const UPDATE: &[&str] = &[
     125              :         "updateEntity",
     126              :         "updateAttrs",
     127              :         "replaceEntity",
     128              :         "replaceAttrs",
     129              :     ];
     130              :     const RETRIEVE: &[&str] = &["retrieveEntity", "queryEntity"];
     131              :     const REDIRECTION: &[&str] = &[
     132              :         "createEntity",
     133              :         "updateEntity",
     134              :         "appendAttrs",
     135              :         "updateAttrs",
     136              :         "deleteAttrs",
     137              :         "deleteEntity",
     138              :         "mergeEntity",
     139              :         "replaceEntity",
     140              :         "replaceAttrs",
     141              :         "retrieveEntity",
     142              :         "queryEntity",
     143              :         "purgeEntity",
     144              :         "retrieveEntityTypes",
     145              :         "retrieveEntityTypeDetails",
     146              :         "retrieveEntityTypeInfo",
     147              :         "retrieveAttrTypes",
     148              :         "retrieveAttrTypeDetails",
     149              :         "retrieveAttrTypeInfo",
     150              :         "retrieveEntityMap",
     151              :         "updateEntityMap",
     152              :         "deleteEntityMap",
     153              :         "createEntityMapQueryEntity",
     154              :         "retrieveContextSourceIdentity",
     155              :     ];
     156        80783 :     match name {
     157        80783 :         "federationOps" => Some(FEDERATION),
     158        30000 :         "associationOps" => Some(ASSOCIATION),
     159        29956 :         "updateOps" => Some(UPDATE),
     160        29934 :         "retrieveOps" => Some(RETRIEVE),
     161        28418 :         "redirectionOps" => Some(REDIRECTION),
     162        25744 :         _ => None,
     163              :     }
     164        80783 : }
     165              : 
     166              : #[cfg(test)]
     167              : mod tests {
     168              :     use super::*;
     169              : 
     170              :     /// Table 4.20-1 as printed: 43 names, no repeats, and the four sections
     171              :     /// in the table's order (provision, consumption, subscription, support).
     172              :     #[test]
     173            2 :     fn table_4_20_1_is_the_whole_vocabulary() {
     174            2 :         assert_eq!(OPERATION_NAMES.len(), 43);
     175            2 :         let mut sorted = OPERATION_NAMES.to_vec();
     176            2 :         sorted.sort_unstable();
     177            2 :         sorted.dedup();
     178            2 :         assert_eq!(sorted.len(), OPERATION_NAMES.len(), "duplicate name");
     179            2 :         assert_eq!(OPERATION_NAMES[0], "createEntity");
     180            2 :         assert_eq!(OPERATION_NAMES[20], "purgeEntity");
     181            2 :         assert_eq!(OPERATION_NAMES[21], "retrieveEntity");
     182            2 :         assert_eq!(OPERATION_NAMES[32], "createSubscription");
     183            2 :         assert_eq!(OPERATION_NAMES[37], "retrieveEntityMap");
     184            2 :         assert_eq!(OPERATION_NAMES[42], "retrieveContextSourceIdentity");
     185            2 :     }
     186              : 
     187              :     /// A group expands only to operations of Table 4.20-1, and a group name
     188              :     /// is never itself an operation name — the two value spaces are
     189              :     /// disjoint, which is what lets a validator accept both lists.
     190              :     #[test]
     191            2 :     fn table_4_20_2_groups_expand_within_the_vocabulary() {
     192           10 :         for g in OPERATION_GROUPS {
     193           10 :             assert!(
     194           10 :                 !OPERATION_NAMES.contains(g),
     195              :                 "{g} is both a group and an operation"
     196              :             );
     197           10 :             let members = group_members(g).unwrap_or_else(|| panic!("{g} has no members"));
     198           10 :             assert!(!members.is_empty());
     199          126 :             for m in members {
     200          126 :                 assert!(OPERATION_NAMES.contains(m), "{g} expands to unknown {m}");
     201              :             }
     202              :         }
     203            2 :         assert_eq!(group_members("federationOps").map(<[&str]>::len), Some(19));
     204            2 :         assert_eq!(group_members("associationOps").map(<[&str]>::len), Some(15));
     205            2 :         assert_eq!(group_members("updateOps").map(<[&str]>::len), Some(4));
     206            2 :         assert_eq!(group_members("retrieveOps").map(<[&str]>::len), Some(2));
     207            2 :         assert_eq!(group_members("redirectionOps").map(<[&str]>::len), Some(23));
     208            2 :         assert_eq!(group_members("retrieveEntity"), None);
     209            2 :         assert_eq!(group_members("notAGroup"), None);
     210            2 :     }
     211              : 
     212              :     /// Table 4.20-2: associationOps is federationOps without the EntityMap
     213              :     /// support operations, and createEntityMapQueryTemporal is in no group.
     214              :     #[test]
     215            2 :     fn association_is_federation_without_the_entity_maps() {
     216            2 :         let fed = group_members("federationOps").expect("federationOps");
     217            2 :         let assoc = group_members("associationOps").expect("associationOps");
     218           38 :         let only_fed: Vec<_> = fed.iter().filter(|m| !assoc.contains(m)).copied().collect();
     219            2 :         assert_eq!(
     220              :             only_fed,
     221              :             [
     222              :                 "retrieveEntityMap",
     223              :                 "updateEntityMap",
     224              :                 "deleteEntityMap",
     225              :                 "createEntityMapQueryEntity"
     226              :             ]
     227              :         );
     228           10 :         for g in OPERATION_GROUPS {
     229           10 :             let members = group_members(g).expect("group");
     230           10 :             assert!(!members.contains(&"createEntityMapQueryTemporal"), "{g}");
     231              :         }
     232            2 :     }
     233              : 
     234              :     /// The default an absent `operations` member stands for is a group name,
     235              :     /// so both readers expand it the same way.
     236              :     #[test]
     237            2 :     fn the_default_is_a_group() {
     238            2 :         assert!(OPERATION_GROUPS.contains(&DEFAULT_OPERATION_GROUP));
     239            2 :         assert!(group_members(DEFAULT_OPERATION_GROUP).is_some());
     240            2 :     }
     241              : }
        

Generated by: LCOV version 2.0-1