reductstore 1.19.6

ReductStore is a time series database designed specifically for storing and managing large amounts of blob data.
Documentation
// Copyright 2021-2026 ReductSoftware UG
// Licensed under the Apache License, Version 2.0

use crate::replication::{ManageReplications, TransactionNotification};
use async_trait::async_trait;
use reduct_base::error::ReductError;
use reduct_base::forbidden;
use reduct_base::msg::replication_api::{
    FullReplicationInfo, ReplicationInfo, ReplicationMode, ReplicationSettings,
};

pub(super) struct ReadOnlyReplicationRepository;

impl ReadOnlyReplicationRepository {
    pub fn new() -> Self {
        ReadOnlyReplicationRepository {}
    }
}

#[async_trait]
impl ManageReplications for ReadOnlyReplicationRepository {
    async fn create_replication(
        &mut self,
        _name: &str,
        _settings: ReplicationSettings,
    ) -> Result<(), ReductError> {
        Err(forbidden!("Cannot create replication in read-only mode"))
    }

    async fn update_replication(
        &mut self,
        _name: &str,
        _settings: ReplicationSettings,
    ) -> Result<(), ReductError> {
        Err(forbidden!("Cannot update replication in read-only mode"))
    }

    async fn replications(&self) -> Result<Vec<ReplicationInfo>, ReductError> {
        Ok(vec![])
    }

    async fn get_info(&self, _name: &str) -> Result<FullReplicationInfo, ReductError> {
        Err(forbidden!("Cannot get replication info in read-only mode"))
    }

    async fn get_replication_settings(
        &self,
        _name: &str,
    ) -> Result<ReplicationSettings, ReductError> {
        Err(forbidden!(
            "Cannot get replication settings in read-only mode"
        ))
    }

    async fn is_replication_running(&self, _name: &str) -> Result<bool, ReductError> {
        Err(forbidden!("Cannot get replication in read-only mode"))
    }

    async fn set_replication_provisioned(
        &mut self,
        _name: &str,
        _provisioned: bool,
    ) -> Result<(), ReductError> {
        Err(forbidden!(
            "Cannot set replication provisioned state in read-only mode"
        ))
    }

    async fn remove_replication(&mut self, _name: &str) -> Result<(), ReductError> {
        Err(forbidden!("Cannot remove replication in read-only mode"))
    }

    async fn set_mode(&mut self, _name: &str, _mode: ReplicationMode) -> Result<(), ReductError> {
        Err(forbidden!(
            "Cannot update replication mode in read-only mode"
        ))
    }

    async fn notify(&mut self, _notification: TransactionNotification) -> Result<(), ReductError> {
        Err(forbidden!("Cannot notify replication in read-only mode"))
    }

    fn start(&mut self) {
        // No-op
    }

    async fn stop(&mut self) {
        // No-op
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::replication::TransactionNotification;
    use rstest::{fixture, rstest};

    #[fixture]
    fn repo() -> ReadOnlyReplicationRepository {
        ReadOnlyReplicationRepository::new()
    }

    mod create {
        use super::*;
        #[rstest]
        #[tokio::test]
        async fn test_create_replication_forbidden(mut repo: ReadOnlyReplicationRepository) {
            let settings = ReplicationSettings::default();
            let result = repo.create_replication("test", settings).await;
            assert_eq!(
                result.err().unwrap(),
                forbidden!("Cannot create replication in read-only mode")
            );
        }
    }

    mod update {
        use super::*;
        #[rstest]
        #[tokio::test]
        async fn test_update_replication_forbidden(mut repo: ReadOnlyReplicationRepository) {
            let settings = ReplicationSettings::default();
            let result = repo.update_replication("test", settings).await;
            assert_eq!(
                result.err().unwrap(),
                forbidden!("Cannot update replication in read-only mode")
            );
        }
    }

    mod replications {
        use super::*;
        #[rstest]
        #[tokio::test]
        async fn test_replications_empty(repo: ReadOnlyReplicationRepository) {
            let reps = repo.replications().await.unwrap();
            assert!(reps.is_empty());
        }
    }

    mod get_info {
        use super::*;
        #[rstest]
        #[tokio::test]
        async fn test_get_info_forbidden(repo: ReadOnlyReplicationRepository) {
            let result = repo.get_info("test").await;
            assert_eq!(
                result.err().unwrap(),
                forbidden!("Cannot get replication info in read-only mode")
            );
        }
    }

    mod get_replication {
        use super::*;
        #[rstest]
        #[tokio::test]
        async fn test_get_replication_settings_forbidden(repo: ReadOnlyReplicationRepository) {
            let err = repo.get_replication_settings("test").await.err().unwrap();
            assert_eq!(
                err,
                forbidden!("Cannot get replication settings in read-only mode")
            );
        }

        #[rstest]
        #[tokio::test]
        async fn test_get_replication_forbidden(repo: ReadOnlyReplicationRepository) {
            let err = repo.is_replication_running("test").await.err().unwrap();
            assert_eq!(err, forbidden!("Cannot get replication in read-only mode"));
        }
    }

    mod get_mut_replication {
        use super::*;
        #[rstest]
        #[tokio::test]
        async fn test_get_mut_replication_forbidden(mut repo: ReadOnlyReplicationRepository) {
            let err = repo
                .set_replication_provisioned("test", true)
                .await
                .err()
                .unwrap();
            assert_eq!(
                err,
                forbidden!("Cannot set replication provisioned state in read-only mode")
            );
        }
    }

    mod notify {
        use super::*;
        use reduct_base::io::RecordMeta;
        #[rstest]
        #[tokio::test]
        async fn test_notify_forbidden(mut repo: ReadOnlyReplicationRepository) {
            let notification = TransactionNotification {
                bucket: "bucket".to_string(),
                entry: "entry".to_string(),
                meta: RecordMeta::builder().timestamp(0).build(),
                event: crate::replication::Transaction::WriteRecord(0),
            };
            let err = repo.notify(notification).await.err().unwrap();
            assert_eq!(
                err,
                forbidden!("Cannot notify replication in read-only mode")
            );
        }
    }

    mod set_mode {
        use super::*;
        #[rstest]
        #[tokio::test]
        async fn test_set_mode_forbidden(mut repo: ReadOnlyReplicationRepository) {
            let err = repo
                .set_mode("test", ReplicationMode::Paused)
                .await
                .err()
                .unwrap();
            assert_eq!(
                err,
                forbidden!("Cannot update replication mode in read-only mode")
            );
        }
    }
}