//! The "web of trust" OpenPGP verification method.

use std::{
    collections::HashSet,
    fmt::Display,
    num::{NonZeroU8, NonZeroUsize},
};

use serde::{Deserialize, Serialize};

use crate::{
    Error,
    common::{ordered_set, set_to_vec},
    config::technology::openpgp::{DomainName, OpenpgpFingerprint},
    file::ConfigWebOfTrustMode,
};

/// Default numerical trust amount for a root in "Web of Trust".
const DEFAULT_ROOT_AMOUNT: NonZeroU8 = NonZeroU8::new(120).expect("120 is greater than 0");

/// Default numerical trust amount for a partially trusted introducer in "Web of Trust".
const DEFAULT_PARTIAL_AMOUNT: NonZeroU8 = NonZeroU8::new(40).expect("40 is greater than 0");

/// Default required flow amount for authentication in "Web of Trust".
const DEFAULT_FLOW_AMOUNT: NonZeroUsize = NonZeroUsize::new(120).expect("120 is greater than 0");

/// The maximum trust amount for partially trusted introducers or trust roots.
pub(crate) const TRUST_AMOUNT_MAX: u8 = 120;

/// The trust amount required for a flow in a "Web of Trust" network to be considered fully trusted.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct TrustAmountFlow(NonZeroUsize);

impl TrustAmountFlow {
    /// Creates a new [`TrustAmountFlow`] from a [`NonZeroUsize`].
    pub fn new(num: NonZeroUsize) -> Self {
        Self(num)
    }

    /// Returns the inner [`NonZeroUsize`].
    pub fn get(&self) -> NonZeroUsize {
        self.0
    }
}

impl Default for TrustAmountFlow {
    fn default() -> Self {
        Self(DEFAULT_FLOW_AMOUNT)
    }
}

impl Display for TrustAmountFlow {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// The trust amount for a root in a "Web of Trust" network.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(try_from = "NonZeroU8")]
pub struct TrustAmountRoot(NonZeroU8);

impl TrustAmountRoot {
    /// Creates a new [`TrustAmountRoot`] from a [`NonZeroU8`].
    ///
    /// # Errors
    ///
    /// Returns an error if `num` is outside the allowed range of 1 - 120.
    pub fn new(num: NonZeroU8) -> Result<Self, Error> {
        if num.get() > TRUST_AMOUNT_MAX {
            return Err(
                crate::config::technology::openpgp::Error::InvalidTrustAmount {
                    amount: num.get().into(),
                }
                .into(),
            );
        }

        Ok(Self(num))
    }

    /// Returns the inner [`NonZeroU8`].
    pub fn get(&self) -> NonZeroU8 {
        self.0
    }
}

impl Default for TrustAmountRoot {
    fn default() -> Self {
        Self(DEFAULT_ROOT_AMOUNT)
    }
}

impl Display for TrustAmountRoot {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl TryFrom<NonZeroU8> for TrustAmountRoot {
    type Error = Error;

    fn try_from(value: NonZeroU8) -> Result<Self, Self::Error> {
        Self::new(value)
    }
}

/// The trust amount for a partially trusted introducer in a "Web of Trust" network.
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(try_from = "NonZeroU8")]
pub struct TrustAmountPartial(NonZeroU8);

impl TrustAmountPartial {
    /// Creates a new [`TrustAmountPartial`] from a [`NonZeroU8`].
    ///
    /// # Errors
    ///
    /// Returns an error if `num` is outside the allowed range of 1 - 120.
    pub fn new(num: NonZeroU8) -> Result<Self, Error> {
        if num.get() > TRUST_AMOUNT_MAX {
            return Err(
                crate::config::technology::openpgp::Error::InvalidTrustAmount {
                    amount: num.get().into(),
                }
                .into(),
            );
        }

        Ok(Self(num))
    }

    /// Returns the inner [`NonZeroU8`].
    pub fn get(&self) -> NonZeroU8 {
        self.0
    }
}

impl Default for TrustAmountPartial {
    fn default() -> Self {
        Self(DEFAULT_PARTIAL_AMOUNT)
    }
}

impl Display for TrustAmountPartial {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl TryFrom<NonZeroU8> for TrustAmountPartial {
    type Error = Error;

    fn try_from(value: NonZeroU8) -> Result<Self, Self::Error> {
        Self::new(value)
    }
}

/// A root for the "Web of Trust".
#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
pub struct WebOfTrustRoot {
    /// The fingerprint of the trust anchor that acts as "Web of Trust" root.
    fingerprint: OpenpgpFingerprint,

    /// The trust amount for the root.
    amount: TrustAmountRoot,
}

impl WebOfTrustRoot {
    /// Creates a new [`WebOfTrustRoot`].
    ///
    /// Takes a [`OpenpgpFingerprint`], which defines the OpenPGP fingerprint of the root and a
    /// [`TrustAmountRoot`] which sets the trust amount for the root.
    pub fn new(fingerprint: OpenpgpFingerprint, amount: TrustAmountRoot) -> Self {
        Self {
            fingerprint,
            amount,
        }
    }

    /// Returns a reference to the [`OpenpgpFingerprint`].
    pub fn fingerprint(&self) -> &OpenpgpFingerprint {
        &self.fingerprint
    }

    /// Returns a reference to the [`TrustAmountRoot`].
    pub fn amount(&self) -> TrustAmountRoot {
        self.amount
    }
}

/// Settings for the "Web of Trust" verification mode.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct WebOfTrustMode {
    /// The required flow amount for authentication for a target identity.
    flow_amount: TrustAmountFlow,

    /// The numerical trust amount of a partially trusted introducer.
    partial_amount: TrustAmountPartial,

    /// The trust roots for the "Web of Trust".
    ///
    /// If a trust root defined in this list cannot be found in the set of trust anchors, a
    /// warning is emitted.
    ///
    /// # Note
    ///
    /// If this list is empty, all trust-anchors found for a specific context are considered to
    /// be trust roots.
    /// If this list is empty and no trust-anchors are found, no artifact
    /// verifiers will be trusted.
    #[serde(serialize_with = "ordered_set")]
    roots: HashSet<WebOfTrustRoot>,

    /// The identity of an artifact verifier must match one of the domains.
    #[serde(serialize_with = "ordered_set")]
    artifact_verifier_identity_domain_matches: HashSet<DomainName>,
}

impl WebOfTrustMode {
    /// Creates a new [`WebOfTrustMode`].
    pub fn new(
        flow_amount: TrustAmountFlow,
        partial_amount: TrustAmountPartial,
        roots: HashSet<WebOfTrustRoot>,
        artifact_verifier_identity_domain_matches: HashSet<DomainName>,
    ) -> Self {
        Self {
            flow_amount,
            partial_amount,
            roots,
            artifact_verifier_identity_domain_matches,
        }
    }

    /// Creates a new [`WebOfTrustMode`] from a [`ConfigWebOfTrustMode`] and explicit defaults.
    ///
    /// # Note
    ///
    /// If found, the trust amount of each [`WebOfTrustRoot`] created from the `config` is
    /// derived from the `defaults` as well.
    /// Otherwise, the implicit default is used instead.
    pub(crate) fn from_config_with_defaults(
        config: &ConfigWebOfTrustMode,
        defaults: &WebOfTrustMode,
    ) -> Self {
        let flow_amount = config.flow_amount().unwrap_or(defaults.flow_amount());
        let partial_amount = config.partial_amount().unwrap_or(defaults.partial_amount());
        let root = if let Some(root) = config.roots() {
            root.iter()
                .map(|root| {
                    let amount = root.amount().unwrap_or_default();
                    WebOfTrustRoot::new(root.fingerprint().clone(), amount)
                })
                .collect::<HashSet<_>>()
        } else {
            defaults.roots().clone()
        };
        let artifact_verifier_identity_domain_matches = config
            .artifact_verifier_identity_domain_matches()
            .unwrap_or(defaults.artifact_verifier_identity_domain_matches())
            .clone();

        Self::new(
            flow_amount,
            partial_amount,
            root,
            artifact_verifier_identity_domain_matches,
        )
    }

    /// Returns the [`TrustAmountFlow`]
    pub fn flow_amount(&self) -> TrustAmountFlow {
        self.flow_amount
    }

    /// Returns the [`TrustAmountPartial`]
    pub fn partial_amount(&self) -> TrustAmountPartial {
        self.partial_amount
    }

    /// Returns a reference to the list of [`WebOfTrustRoot`] entries.
    pub fn roots(&self) -> &HashSet<WebOfTrustRoot> {
        &self.roots
    }

    /// Returns the [`TrustAmountRoot`] for a `root` if it matches the provided `fingerprint`.
    pub fn root_trust_amount(&self, fingerprint: &OpenpgpFingerprint) -> Option<TrustAmountRoot> {
        self.roots
            .iter()
            .filter_map(|root| {
                if root.fingerprint() == fingerprint {
                    Some(root.amount())
                } else {
                    None
                }
            })
            .next()
    }

    /// Returns a reference to the list of [`DomainName`] entries.
    pub fn artifact_verifier_identity_domain_matches(&self) -> &HashSet<DomainName> {
        &self.artifact_verifier_identity_domain_matches
    }
}

impl Default for WebOfTrustMode {
    fn default() -> Self {
        Self::new(
            TrustAmountFlow::default(),
            TrustAmountPartial::default(),
            HashSet::new(),
            HashSet::new(),
        )
    }
}

impl Display for WebOfTrustMode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        writeln!(
            f,
            "✅ Each artifact is verified using the \"web of trust\" verification method.\n"
        )?;
        writeln!(
            f,
            "↔️ Each partially trusted introducer has a trust amount of {}.\n",
            self.partial_amount()
        )?;

        if !self.artifact_verifier_identity_domain_matches().is_empty() {
            writeln!(
                f,
                "📧 A valid certificate must have a valid User ID that uses one of the following domains and reaches a flow amount of {} for the certificate to be considered as artifact verifier:",
                self.flow_amount()
            )?;
            for domain_name in set_to_vec(self.artifact_verifier_identity_domain_matches()).iter() {
                writeln!(f, "⤷ {domain_name}")?;
            }
            writeln!(f)?;
        } else {
            writeln!(
                f,
                "📧 A valid certificate is not required to use a specific domain in any of its User IDs, but one of them must reach a flow amount of {} for the certificate to be considered as artifact verifier.\n",
                self.flow_amount()
            )?;
        }

        if !self.roots().is_empty() {
            writeln!(
                f,
                "🐾 Only a valid certificate that matches one of the following OpenPGP fingerprints is used as trust root with the respective trust amount:"
            )?;
            for trust_root in set_to_vec(self.roots()).iter() {
                writeln!(
                    f,
                    "⤷ {} ({})",
                    trust_root.fingerprint(),
                    trust_root.amount()
                )?;
            }
        } else {
            writeln!(
                f,
                "🐾 Each valid certificate is considered as trust root with a default trust amount of {}.",
                TrustAmountRoot::default()
            )?;
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use rstest::rstest;
    use testresult::TestResult;

    use super::*;
    use crate::file::ConfigWebOfTrustRoot;

    /// Ensures that [`WebOfTrustMode::from_config_with_defaults`] behaves consistently.
    #[rstest]
    #[case::all_defaults(
        ConfigWebOfTrustMode::default(),
        WebOfTrustMode::default(),
        WebOfTrustMode::default()
    )]
    #[rstest]
    #[case::all_defaults(
        ConfigWebOfTrustMode::default(),
        WebOfTrustMode::default(),
        WebOfTrustMode::default()
    )]
    #[case::default_config_and_custom_defaults(
        ConfigWebOfTrustMode::default(),
        WebOfTrustMode::new(
            TrustAmountFlow::new(NonZeroUsize::new(140).expect("140 is larger than 0")),
            TrustAmountPartial::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
            HashSet::from_iter([
                WebOfTrustRoot::new(
                    "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15".parse()?,
                    TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
                ),
                WebOfTrustRoot::new(
                    "e242ed3bffccdf271b7fbaf34ed72d089537b42f".parse()?,
                    TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
                ),
                WebOfTrustRoot::new(
                    "6eadeac2dade6347e87c0d24fd455feffa7069f0".parse()?,
                    TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
                ),
            ]),
            HashSet::from_iter(["example.org".parse()?, "sub.example.org".parse()?]),
        ),
        WebOfTrustMode::new(
            TrustAmountFlow::new(NonZeroUsize::new(140).expect("140 is larger than 0")),
            TrustAmountPartial::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
            HashSet::from_iter([
                WebOfTrustRoot::new(
                    "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15".parse()?,
                    TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
                ),
                WebOfTrustRoot::new(
                    "e242ed3bffccdf271b7fbaf34ed72d089537b42f".parse()?,
                    TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
                ),
                WebOfTrustRoot::new(
                    "6eadeac2dade6347e87c0d24fd455feffa7069f0".parse()?,
                    TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
                ),
            ]),
            HashSet::from_iter(["example.org".parse()?, "sub.example.org".parse()?]),
        ),
    )]
    #[case::empty_list_config_custom_defaults(
        ConfigWebOfTrustMode::new(
            Some(TrustAmountFlow::new(NonZeroUsize::new(140).expect("140 is larger than 0"))),
            Some(TrustAmountPartial::new(NonZeroU8::new(50).expect("50 is larger than 0"))?),
            Some(HashSet::new()),
            Some(HashSet::new()),
        ),
        WebOfTrustMode::new(
            TrustAmountFlow::new(NonZeroUsize::new(140).expect("140 is larger than 0")),
            TrustAmountPartial::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
            HashSet::from_iter([
                WebOfTrustRoot::new(
                    "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15".parse()?,
                    TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
                ),
                WebOfTrustRoot::new(
                    "e242ed3bffccdf271b7fbaf34ed72d089537b42f".parse()?,
                    TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
                ),
                WebOfTrustRoot::new(
                    "6eadeac2dade6347e87c0d24fd455feffa7069f0".parse()?,
                    TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
                ),
            ]),
            HashSet::from_iter(["example.org".parse()?, "sub.example.org".parse()?]),
        ),
        WebOfTrustMode::new(
            TrustAmountFlow::new(NonZeroUsize::new(140).expect("140 is larger than 0")),
            TrustAmountPartial::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
            HashSet::new(),
            HashSet::new(),
        ),
    )]
    #[case::fully_custom_config_with_default_defaults(
        ConfigWebOfTrustMode::new(
            Some(TrustAmountFlow::new(NonZeroUsize::new(140).expect("140 is larger than 0"))),
            Some(TrustAmountPartial::new(NonZeroU8::new(50).expect("50 is larger than 0"))?),
            Some(HashSet::from_iter([
                ConfigWebOfTrustRoot::new(
                    "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15".parse()?,
                    Some(TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?),
                ),
                ConfigWebOfTrustRoot::new(
                    "e242ed3bffccdf271b7fbaf34ed72d089537b42f".parse()?,
                    Some(TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?),
                ),
                ConfigWebOfTrustRoot::new(
                    "6eadeac2dade6347e87c0d24fd455feffa7069f0".parse()?,
                    Some(TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?),
                ),
            ])),
            Some(HashSet::from_iter(["example.org".parse()?, "sub.example.org".parse()?])),
        ),
        WebOfTrustMode::default(),
        WebOfTrustMode::new(
            TrustAmountFlow::new(NonZeroUsize::new(140).expect("140 is larger than 0")),
            TrustAmountPartial::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
            HashSet::from_iter([
                WebOfTrustRoot::new(
                    "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15".parse()?,
                    TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
                ),
                WebOfTrustRoot::new(
                    "e242ed3bffccdf271b7fbaf34ed72d089537b42f".parse()?,
                    TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
                ),
                WebOfTrustRoot::new(
                    "6eadeac2dade6347e87c0d24fd455feffa7069f0".parse()?,
                    TrustAmountRoot::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
                ),
            ]),
            HashSet::from_iter(["example.org".parse()?, "sub.example.org".parse()?]),
        ),
    )]
    #[case::mixed_custom_config_with_custom_defaults(
        ConfigWebOfTrustMode::new(
            None,
            None,
            Some(HashSet::from_iter([
                ConfigWebOfTrustRoot::new(
                    "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15".parse()?,
                    None,
                ),
                ConfigWebOfTrustRoot::new(
                    "e242ed3bffccdf271b7fbaf34ed72d089537b42f".parse()?,
                    None,
                ),
                ConfigWebOfTrustRoot::new(
                    "6eadeac2dade6347e87c0d24fd455feffa7069f0".parse()?,
                    None,
                ),
            ])),
            Some(HashSet::from_iter(["example.org".parse()?, "sub.example.org".parse()?])),
        ),
        WebOfTrustMode::new(
            TrustAmountFlow::new(NonZeroUsize::new(140).expect("140 is larger than 0")),
            TrustAmountPartial::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
            HashSet::from_iter([
                WebOfTrustRoot::new(
                    "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15".parse()?,
                    TrustAmountRoot::default(),
                ),
                WebOfTrustRoot::new(
                    "e242ed3bffccdf271b7fbaf34ed72d089537b42f".parse()?,
                    TrustAmountRoot::default(),
                ),
                WebOfTrustRoot::new(
                    "6eadeac2dade6347e87c0d24fd455feffa7069f0".parse()?,
                    TrustAmountRoot::default(),
                ),
            ]),
            HashSet::from_iter(["example.org".parse()?, "sub.example.org".parse()?]),
        ),
        WebOfTrustMode::new(
            TrustAmountFlow::new(NonZeroUsize::new(140).expect("140 is larger than 0")),
            TrustAmountPartial::new(NonZeroU8::new(50).expect("50 is larger than 0"))?,
            HashSet::from_iter([
                WebOfTrustRoot::new(
                    "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15".parse()?,
                    TrustAmountRoot::default(),
                ),
                WebOfTrustRoot::new(
                    "e242ed3bffccdf271b7fbaf34ed72d089537b42f".parse()?,
                    TrustAmountRoot::default(),
                ),
                WebOfTrustRoot::new(
                    "6eadeac2dade6347e87c0d24fd455feffa7069f0".parse()?,
                    TrustAmountRoot::default(),
                ),
            ]),
            HashSet::from_iter(["example.org".parse()?, "sub.example.org".parse()?]),
        )
    )]
    fn web_of_trust_mode_from_config_with_defaults(
        #[case] config: ConfigWebOfTrustMode,
        #[case] defaults: WebOfTrustMode,
        #[case] output: WebOfTrustMode,
    ) -> TestResult {
        assert_eq!(
            WebOfTrustMode::from_config_with_defaults(&config, &defaults),
            output
        );
        Ok(())
    }

    #[test]
    fn trust_amount_flow_get() {
        assert_eq!(TrustAmountFlow::default().get().get(), 120);
    }

    #[test]
    fn trust_amount_partial_get() {
        assert_eq!(TrustAmountPartial::default().get().get(), 40);
    }

    #[test]
    fn trust_amount_root_get() {
        assert_eq!(TrustAmountRoot::default().get().get(), 120);
    }

    #[test]
    fn web_of_trust_root_fingerprint() -> TestResult {
        let fingerprint: OpenpgpFingerprint = "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15".parse()?;
        assert_eq!(
            WebOfTrustRoot::new(fingerprint.clone(), TrustAmountRoot::default()).fingerprint(),
            &fingerprint
        );

        Ok(())
    }

    #[test]
    fn web_of_trust_root_amount() -> TestResult {
        assert_eq!(
            WebOfTrustRoot::new(
                "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15".parse()?,
                TrustAmountRoot::default()
            )
            .amount(),
            TrustAmountRoot::default()
        );

        Ok(())
    }

    #[test]
    fn web_of_trust_mode_root() -> TestResult {
        let root = HashSet::from_iter([
            WebOfTrustRoot::new(
                "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15".parse()?,
                TrustAmountRoot::default(),
            ),
            WebOfTrustRoot::new(
                "e242ed3bffccdf271b7fbaf34ed72d089537b42f".parse()?,
                TrustAmountRoot::default(),
            ),
            WebOfTrustRoot::new(
                "6eadeac2dade6347e87c0d24fd455feffa7069f0".parse()?,
                TrustAmountRoot::default(),
            ),
        ]);
        let mode = WebOfTrustMode::new(
            TrustAmountFlow::default(),
            TrustAmountPartial::default(),
            root.clone(),
            HashSet::from_iter(["example.org".parse()?, "sub.example.org".parse()?]),
        );

        assert_eq!(mode.roots(), &root);

        Ok(())
    }

    #[rstest]
    #[case::matches("f1d2d2f924e986ac86fdf7b36c94bcdf32beec15".parse()?, Some(TrustAmountRoot::default()))]
    #[case::does_not_match("e242ed3bffccdf271b7fbaf34ed72d089537b42f".parse()?, None)]
    fn web_of_trust_mode_root_trust_amount(
        #[case] fingerprint: OpenpgpFingerprint,
        #[case] amount: Option<TrustAmountRoot>,
    ) -> TestResult {
        let mode = WebOfTrustMode::new(
            TrustAmountFlow::default(),
            TrustAmountPartial::default(),
            HashSet::from_iter([WebOfTrustRoot::new(
                "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15".parse()?,
                TrustAmountRoot::default(),
            )]),
            HashSet::from_iter(["example.org".parse()?, "sub.example.org".parse()?]),
        );

        assert_eq!(mode.root_trust_amount(&fingerprint), amount);

        Ok(())
    }

    #[test]
    fn web_of_trust_mode_artifact_verifier_identity_domain_matches() -> TestResult {
        let domain_matches =
            HashSet::from_iter(["example.org".parse()?, "sub.example.org".parse()?]);
        let mode = WebOfTrustMode::new(
            TrustAmountFlow::default(),
            TrustAmountPartial::default(),
            HashSet::new(),
            domain_matches.clone(),
        );

        assert_eq!(
            mode.artifact_verifier_identity_domain_matches(),
            &domain_matches
        );

        Ok(())
    }

    #[test]
    fn trust_amount_partial_new_fails_on_trust_amount_too_large() {
        assert!(
            TrustAmountPartial::new(NonZeroU8::new(121).expect("121 is larger than 0")).is_err()
        )
    }

    #[test]
    fn trust_amount_root_new_fails_on_trust_amount_too_large() {
        assert!(TrustAmountRoot::new(NonZeroU8::new(121).expect("121 is larger than 0")).is_err())
    }
}
