@@ -85,6 +85,13 @@ impl Logger {
8585 . context ( AppendSnafu {
8686 path : self . path . to_path_buf ( ) ,
8787 } ) ?;
88+ handle. write ( b"\n " ) . await . context ( AppendSnafu {
89+ path : self . path . to_path_buf ( ) ,
90+ } ) ?;
91+
92+ handle. flush ( ) . await . context ( IOSnafu {
93+ path : self . path . to_path_buf ( ) ,
94+ } ) ?;
8895
8996 Ok ( ( ) )
9097 }
@@ -114,3 +121,103 @@ impl Logger {
114121 . collect ( )
115122 }
116123}
124+
125+ #[ cfg( test) ]
126+ mod tests {
127+ use async_tempfile:: TempFile ;
128+ use camino:: Utf8PathBuf ;
129+ use chrono:: Utc ;
130+ use tokio:: task:: JoinSet ;
131+
132+ use super :: * ;
133+ use crate :: database:: operation:: * ;
134+ use crate :: extractors:: user:: User ;
135+ use crate :: routes:: category:: CategoryForm ;
136+
137+ #[ tokio:: test]
138+ async fn many_writers ( ) {
139+ let mut set = JoinSet :: new ( ) ;
140+ let tmpfile =
141+ Utf8PathBuf :: from_path_buf ( TempFile :: new ( ) . await . unwrap ( ) . file_path ( ) . to_path_buf ( ) )
142+ . unwrap ( ) ;
143+ let logger = Logger :: new ( tmpfile. clone ( ) ) . await . unwrap ( ) ;
144+
145+ let operation_log = OperationLog {
146+ user : Some ( User ( "foo" . to_string ( ) ) ) ,
147+ date : Utc :: now ( ) ,
148+ table : Table :: Category ,
149+ operation : OperationType :: Create ,
150+ operation_id : OperationId {
151+ name : "object" . to_string ( ) ,
152+ object_id : 1 ,
153+ } ,
154+ operation_form : Operation :: Category ( CategoryForm {
155+ name : "object" . to_string ( ) ,
156+ path : "path" . to_string ( ) ,
157+ } ) ,
158+ } ;
159+
160+ for _i in 0 ..100 {
161+ let logger = logger. clone ( ) ;
162+ let operation_log = operation_log. clone ( ) ;
163+ set. spawn ( async move { logger. write ( operation_log) . await } ) ;
164+ }
165+
166+ for task in set. join_all ( ) . await {
167+ assert ! ( task. is_ok( ) ) ;
168+ }
169+
170+ let s = tokio:: fs:: read_to_string ( & tmpfile) . await . unwrap ( ) ;
171+ println ! ( "{s}" ) ;
172+
173+ let logs = logger. read ( ) . await . unwrap ( ) ;
174+ assert_eq ! ( logs. len( ) , 100 ) ;
175+ }
176+
177+ #[ tokio:: test]
178+ async fn mixed_readers_writers ( ) {
179+ let mut set = JoinSet :: new ( ) ;
180+ let tmpfile =
181+ Utf8PathBuf :: from_path_buf ( TempFile :: new ( ) . await . unwrap ( ) . file_path ( ) . to_path_buf ( ) )
182+ . unwrap ( ) ;
183+ let logger = Logger :: new ( tmpfile. clone ( ) ) . await . unwrap ( ) ;
184+
185+ let operation_log = OperationLog {
186+ user : Some ( User ( "foo" . to_string ( ) ) ) ,
187+ date : Utc :: now ( ) ,
188+ table : Table :: Category ,
189+ operation : OperationType :: Create ,
190+ operation_id : OperationId {
191+ name : "object" . to_string ( ) ,
192+ object_id : 1 ,
193+ } ,
194+ operation_form : Operation :: Category ( CategoryForm {
195+ name : "object" . to_string ( ) ,
196+ path : "path" . to_string ( ) ,
197+ } ) ,
198+ } ;
199+
200+ for i in 0 ..200 {
201+ let logger = logger. clone ( ) ;
202+ if i % 2 == 0 {
203+ let operation_log = operation_log. clone ( ) ;
204+ set. spawn ( async move { logger. write ( operation_log) . await } ) ;
205+ } else {
206+ set. spawn ( async move {
207+ let _ = logger. read ( ) . await ?;
208+ Ok ( ( ) )
209+ } ) ;
210+ }
211+ }
212+
213+ for task in set. join_all ( ) . await {
214+ assert ! ( task. is_ok( ) ) ;
215+ }
216+
217+ let s = tokio:: fs:: read_to_string ( & tmpfile) . await . unwrap ( ) ;
218+ println ! ( "{s}" ) ;
219+
220+ let logs = logger. read ( ) . await . unwrap ( ) ;
221+ assert_eq ! ( logs. len( ) , 100 ) ;
222+ }
223+ }
0 commit comments