@@ -213,3 +213,277 @@ impl Item {
213213 Ok ( ( ) )
214214 }
215215}
216+
217+ #[ cfg( test) ]
218+ mod tests {
219+ use std:: sync:: Arc ;
220+
221+ use oo7:: dbus;
222+
223+ use super :: * ;
224+
225+ /// Helper to create a peer-to-peer connection pair using Unix socket
226+ async fn create_p2p_connection ( ) -> ( zbus:: Connection , zbus:: Connection ) {
227+ let guid = zbus:: Guid :: generate ( ) ;
228+ let ( p0, p1) = tokio:: net:: UnixStream :: pair ( ) . unwrap ( ) ;
229+
230+ let ( client_conn, server_conn) = tokio:: try_join!(
231+ zbus:: connection:: Builder :: unix_stream( p0) . p2p( ) . build( ) ,
232+ zbus:: connection:: Builder :: unix_stream( p1)
233+ . server( guid)
234+ . unwrap( )
235+ . p2p( )
236+ . build( ) ,
237+ )
238+ . unwrap ( ) ;
239+
240+ ( server_conn, client_conn)
241+ }
242+
243+ #[ tokio:: test]
244+ async fn label_property ( ) {
245+ let ( server_conn, client_conn) = create_p2p_connection ( ) . await ;
246+
247+ let _server = Service :: run_with_connection (
248+ server_conn,
249+ Some ( oo7:: Secret :: from ( "test-password-long-enough" ) ) ,
250+ )
251+ . await
252+ . unwrap ( ) ;
253+
254+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 50 ) ) . await ;
255+
256+ let service_api = dbus:: api:: Service :: new ( & client_conn) . await . unwrap ( ) ;
257+ let ( _aes_key, session) = service_api. open_session ( None ) . await . unwrap ( ) ;
258+ let session = Arc :: new ( session) ;
259+
260+ let collections = service_api. collections ( ) . await . unwrap ( ) ;
261+ let secret = oo7:: Secret :: text ( "test-secret" ) ;
262+ let attributes = & [ ( "app" , "test" ) ] ;
263+ let dbus_secret = dbus:: api:: DBusSecret :: new ( session, secret) ;
264+
265+ let item = collections[ 0 ]
266+ . create_item ( "Original Label" , attributes, & dbus_secret, false , None )
267+ . await
268+ . unwrap ( ) ;
269+
270+ // Get label
271+ let label = item. label ( ) . await . unwrap ( ) ;
272+ assert_eq ! ( label, "Original Label" ) ;
273+
274+ // Set label
275+ item. set_label ( "New Label" ) . await . unwrap ( ) ;
276+
277+ // Verify new label
278+ let label = item. label ( ) . await . unwrap ( ) ;
279+ assert_eq ! ( label, "New Label" ) ;
280+ }
281+
282+ #[ tokio:: test]
283+ async fn attributes_property ( ) {
284+ let ( server_conn, client_conn) = create_p2p_connection ( ) . await ;
285+
286+ let _server = Service :: run_with_connection (
287+ server_conn,
288+ Some ( oo7:: Secret :: from ( "test-password-long-enough" ) ) ,
289+ )
290+ . await
291+ . unwrap ( ) ;
292+
293+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 50 ) ) . await ;
294+
295+ let service_api = dbus:: api:: Service :: new ( & client_conn) . await . unwrap ( ) ;
296+ let ( _aes_key, session) = service_api. open_session ( None ) . await . unwrap ( ) ;
297+ let session = Arc :: new ( session) ;
298+
299+ let collections = service_api. collections ( ) . await . unwrap ( ) ;
300+ let secret = oo7:: Secret :: text ( "test-secret" ) ;
301+ let attributes = & [ ( "app" , "firefox" ) , ( "username" , "user@example.com" ) ] ;
302+ let dbus_secret = dbus:: api:: DBusSecret :: new ( session, secret) ;
303+
304+ let item = collections[ 0 ]
305+ . create_item ( "Test Item" , attributes, & dbus_secret, false , None )
306+ . await
307+ . unwrap ( ) ;
308+
309+ // Get attributes
310+ let attrs = item. attributes ( ) . await . unwrap ( ) ;
311+ assert_eq ! ( attrs. get( "app" ) . unwrap( ) , "firefox" ) ;
312+ assert_eq ! ( attrs. get( "username" ) . unwrap( ) , "user@example.com" ) ;
313+
314+ // Set new attributes
315+ item. set_attributes ( & [ ( "app" , "chrome" ) , ( "username" , "newuser@example.com" ) ] )
316+ . await
317+ . unwrap ( ) ;
318+
319+ // Verify new attributes
320+ let attrs = item. attributes ( ) . await . unwrap ( ) ;
321+ assert_eq ! ( attrs. get( "app" ) . unwrap( ) , "chrome" ) ;
322+ assert_eq ! ( attrs. get( "username" ) . unwrap( ) , "newuser@example.com" ) ;
323+ }
324+
325+ #[ tokio:: test]
326+ async fn timestamps ( ) {
327+ let ( server_conn, client_conn) = create_p2p_connection ( ) . await ;
328+
329+ let _server = Service :: run_with_connection (
330+ server_conn,
331+ Some ( oo7:: Secret :: from ( "test-password-long-enough" ) ) ,
332+ )
333+ . await
334+ . unwrap ( ) ;
335+
336+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 50 ) ) . await ;
337+
338+ let service_api = dbus:: api:: Service :: new ( & client_conn) . await . unwrap ( ) ;
339+ let ( _aes_key, session) = service_api. open_session ( None ) . await . unwrap ( ) ;
340+ let session = Arc :: new ( session) ;
341+
342+ let collections = service_api. collections ( ) . await . unwrap ( ) ;
343+ let secret = oo7:: Secret :: text ( "test-secret" ) ;
344+ let attributes = & [ ( "app" , "test" ) ] ;
345+ let dbus_secret = dbus:: api:: DBusSecret :: new ( session, secret) ;
346+
347+ let item = collections[ 0 ]
348+ . create_item ( "Test Item" , attributes, & dbus_secret, false , None )
349+ . await
350+ . unwrap ( ) ;
351+
352+ // Get created timestamp
353+ let created = item. created ( ) . await . unwrap ( ) ;
354+ assert ! ( created. as_secs( ) > 0 , "Created timestamp should be set" ) ;
355+
356+ // Get modified timestamp
357+ let modified = item. modified ( ) . await . unwrap ( ) ;
358+ assert ! ( modified. as_secs( ) > 0 , "Modified timestamp should be set" ) ;
359+
360+ // Created and modified should be close (within a second for new item)
361+ let diff = if created > modified {
362+ created. as_secs ( ) - modified. as_secs ( )
363+ } else {
364+ modified. as_secs ( ) - created. as_secs ( )
365+ } ;
366+ assert ! ( diff <= 1 , "Created and modified should be within 1 second" ) ;
367+ }
368+
369+ #[ tokio:: test]
370+ async fn secret_retrieval_plain ( ) {
371+ let ( server_conn, client_conn) = create_p2p_connection ( ) . await ;
372+
373+ let _server = Service :: run_with_connection (
374+ server_conn,
375+ Some ( oo7:: Secret :: from ( "test-password-long-enough" ) ) ,
376+ )
377+ . await
378+ . unwrap ( ) ;
379+
380+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 50 ) ) . await ;
381+
382+ let service_api = dbus:: api:: Service :: new ( & client_conn) . await . unwrap ( ) ;
383+ let ( _aes_key, session) = service_api. open_session ( None ) . await . unwrap ( ) ;
384+ let session = Arc :: new ( session) ;
385+
386+ let collections = service_api. collections ( ) . await . unwrap ( ) ;
387+ let secret = oo7:: Secret :: text ( "my-secret-password" ) ;
388+ let attributes = & [ ( "app" , "test" ) ] ;
389+ let dbus_secret = dbus:: api:: DBusSecret :: new ( Arc :: clone ( & session) , secret. clone ( ) ) ;
390+
391+ let item = collections[ 0 ]
392+ . create_item ( "Test Item" , attributes, & dbus_secret, false , None )
393+ . await
394+ . unwrap ( ) ;
395+
396+ // Retrieve secret
397+ let retrieved_secret = item. secret ( & session) . await . unwrap ( ) ;
398+ assert_eq ! ( retrieved_secret. value( ) , secret. as_bytes( ) ) ;
399+ }
400+
401+ #[ tokio:: test]
402+ async fn secret_retrieval_encrypted ( ) {
403+ let ( server_conn, client_conn) = create_p2p_connection ( ) . await ;
404+
405+ let _server = Service :: run_with_connection (
406+ server_conn,
407+ Some ( oo7:: Secret :: from ( "test-password-long-enough" ) ) ,
408+ )
409+ . await
410+ . unwrap ( ) ;
411+
412+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 50 ) ) . await ;
413+
414+ let service_api = dbus:: api:: Service :: new ( & client_conn) . await . unwrap ( ) ;
415+
416+ // Open encrypted session
417+ let client_private_key = oo7:: Key :: generate_private_key ( ) . unwrap ( ) ;
418+ let client_public_key = oo7:: Key :: generate_public_key ( & client_private_key) . unwrap ( ) ;
419+
420+ let ( server_public_key_opt, session) = service_api
421+ . open_session ( Some ( client_public_key) )
422+ . await
423+ . unwrap ( ) ;
424+
425+ let server_public_key = server_public_key_opt. unwrap ( ) ;
426+ let aes_key = oo7:: Key :: generate_aes_key ( & client_private_key, & server_public_key) . unwrap ( ) ;
427+ let session = Arc :: new ( session) ;
428+
429+ let collections = service_api. collections ( ) . await . unwrap ( ) ;
430+ let secret = oo7:: Secret :: text ( "my-encrypted-secret" ) ;
431+ let attributes = & [ ( "app" , "test" ) ] ;
432+ let dbus_secret =
433+ dbus:: api:: DBusSecret :: new_encrypted ( Arc :: clone ( & session) , secret. clone ( ) , & aes_key)
434+ . unwrap ( ) ;
435+
436+ let item = collections[ 0 ]
437+ . create_item ( "Test Item" , attributes, & dbus_secret, false , None )
438+ . await
439+ . unwrap ( ) ;
440+
441+ // Retrieve secret
442+ let retrieved_secret = item. secret ( & session) . await . unwrap ( ) ;
443+ assert_eq ! (
444+ retrieved_secret
445+ . decrypt( Some ( & Arc :: new( client_private_key) ) )
446+ . unwrap( ) ,
447+ secret
448+ ) ;
449+ }
450+
451+ #[ tokio:: test]
452+ async fn delete_item ( ) {
453+ let ( server_conn, client_conn) = create_p2p_connection ( ) . await ;
454+
455+ let _server = Service :: run_with_connection (
456+ server_conn,
457+ Some ( oo7:: Secret :: from ( "test-password-long-enough" ) ) ,
458+ )
459+ . await
460+ . unwrap ( ) ;
461+
462+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_millis ( 50 ) ) . await ;
463+
464+ let service_api = dbus:: api:: Service :: new ( & client_conn) . await . unwrap ( ) ;
465+ let ( _aes_key, session) = service_api. open_session ( None ) . await . unwrap ( ) ;
466+ let session = Arc :: new ( session) ;
467+
468+ let collections = service_api. collections ( ) . await . unwrap ( ) ;
469+ let secret = oo7:: Secret :: text ( "test-secret" ) ;
470+ let attributes = & [ ( "app" , "test" ) ] ;
471+ let dbus_secret = dbus:: api:: DBusSecret :: new ( session, secret) ;
472+
473+ let item = collections[ 0 ]
474+ . create_item ( "Test Item" , attributes, & dbus_secret, false , None )
475+ . await
476+ . unwrap ( ) ;
477+
478+ // Verify item exists
479+ let items = collections[ 0 ] . items ( ) . await . unwrap ( ) ;
480+ assert_eq ! ( items. len( ) , 1 ) ;
481+
482+ // Delete item
483+ item. delete ( None ) . await . unwrap ( ) ;
484+
485+ // Verify item is deleted
486+ let items = collections[ 0 ] . items ( ) . await . unwrap ( ) ;
487+ assert_eq ! ( items. len( ) , 0 , "Item should be deleted from collection" ) ;
488+ }
489+ }
0 commit comments