reinhardt_db/migrations/
repository.rs1pub mod filesystem;
6
7use super::{Migration, MigrationError, Result};
8use async_trait::async_trait;
9
10#[async_trait]
16pub trait MigrationRepository: Send + Sync {
17 async fn save(&mut self, migration: &Migration) -> Result<()>;
19
20 async fn get(&self, app_label: &str, name: &str) -> Result<Migration>;
22
23 async fn list(&self, app_label: &str) -> Result<Vec<Migration>>;
25
26 async fn exists(&self, app_label: &str, name: &str) -> Result<bool> {
28 self.get(app_label, name).await.map(|_| true).or(Ok(false))
29 }
30
31 async fn delete(&mut self, _app_label: &str, _name: &str) -> Result<()> {
33 Err(MigrationError::InvalidMigration(
34 "Delete operation not supported by this repository".to_string(),
35 ))
36 }
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42 use std::collections::HashMap;
43
44 fn create_test_migration(app_label: &str, name: &str) -> Migration {
46 Migration {
47 app_label: app_label.to_string(),
48 name: name.to_string(),
49 operations: vec![],
50 dependencies: vec![],
51 atomic: true,
52 initial: None,
53 replaces: vec![],
54 state_only: false,
55 database_only: false,
56 swappable_dependencies: vec![],
57 optional_dependencies: vec![],
58 }
59 }
60
61 struct TestRepository {
63 migrations: HashMap<(String, String), Migration>,
64 }
65
66 impl TestRepository {
67 fn new() -> Self {
68 Self {
69 migrations: HashMap::new(),
70 }
71 }
72 }
73
74 #[async_trait]
75 impl MigrationRepository for TestRepository {
76 async fn save(&mut self, migration: &Migration) -> Result<()> {
77 let key = (migration.app_label.to_string(), migration.name.to_string());
78 self.migrations.insert(key, migration.clone());
79 Ok(())
80 }
81
82 async fn get(&self, app_label: &str, name: &str) -> Result<Migration> {
83 let key = (app_label.to_string(), name.to_string());
84 self.migrations
85 .get(&key)
86 .cloned()
87 .ok_or_else(|| MigrationError::NotFound(format!("{}.{}", app_label, name)))
88 }
89
90 async fn list(&self, app_label: &str) -> Result<Vec<Migration>> {
91 Ok(self
92 .migrations
93 .values()
94 .filter(|m| m.app_label == app_label)
95 .cloned()
96 .collect())
97 }
98
99 async fn delete(&mut self, app_label: &str, name: &str) -> Result<()> {
100 let key = (app_label.to_string(), name.to_string());
101 self.migrations
102 .remove(&key)
103 .ok_or_else(|| MigrationError::NotFound(format!("{}.{}", app_label, name)))?;
104 Ok(())
105 }
106 }
107
108 #[tokio::test]
109 async fn test_save_and_get() {
110 let mut repo = TestRepository::new();
111 let migration = create_test_migration("polls", "0001_initial");
112
113 repo.save(&migration).await.unwrap();
114
115 let retrieved = repo.get("polls", "0001_initial").await.unwrap();
116 assert_eq!(retrieved.app_label, "polls");
117 assert_eq!(retrieved.name, "0001_initial");
118 }
119
120 #[tokio::test]
121 async fn test_list() {
122 let mut repo = TestRepository::new();
123 repo.save(&create_test_migration("polls", "0001_initial"))
124 .await
125 .unwrap();
126 repo.save(&create_test_migration("polls", "0002_add_field"))
127 .await
128 .unwrap();
129 repo.save(&create_test_migration("users", "0001_initial"))
130 .await
131 .unwrap();
132
133 let polls_migrations = repo.list("polls").await.unwrap();
134 assert_eq!(polls_migrations.len(), 2);
135 assert!(polls_migrations.iter().all(|m| m.app_label == "polls"));
136
137 let users_migrations = repo.list("users").await.unwrap();
138 assert_eq!(users_migrations.len(), 1);
139 }
140
141 #[tokio::test]
142 async fn test_exists() {
143 let mut repo = TestRepository::new();
144 repo.save(&create_test_migration("polls", "0001_initial"))
145 .await
146 .unwrap();
147
148 assert!(repo.exists("polls", "0001_initial").await.unwrap());
149 assert!(!repo.exists("polls", "0002_nonexistent").await.unwrap());
150 }
151
152 #[tokio::test]
153 async fn test_delete() {
154 let mut repo = TestRepository::new();
155 repo.save(&create_test_migration("polls", "0001_initial"))
156 .await
157 .unwrap();
158
159 assert!(repo.exists("polls", "0001_initial").await.unwrap());
160
161 repo.delete("polls", "0001_initial").await.unwrap();
162
163 assert!(!repo.exists("polls", "0001_initial").await.unwrap());
164 }
165
166 #[tokio::test]
167 async fn test_get_not_found() {
168 let repo = TestRepository::new();
169 let result = repo.get("polls", "0001_nonexistent").await;
170 assert!(result.is_err());
171 assert!(matches!(result.unwrap_err(), MigrationError::NotFound(_)));
172 }
173}