2021-06-23 16:38:54 +03:00
use std ::path ::PathBuf ;
use anyhow ::{ bail , format_err , Error } ;
2021-06-23 16:38:57 +03:00
use proxmox_apt ::repositories ::{
2024-07-02 10:37:30 +03:00
check_repositories , get_current_release_codename , standard_repositories , DebianCodename ,
2021-06-23 16:38:57 +03:00
} ;
2024-07-01 11:32:41 +03:00
use proxmox_apt ::repositories ::{
2024-07-02 10:37:30 +03:00
APTRepositoryFileImpl , APTRepositoryImpl , APTStandardRepositoryImpl ,
} ;
use proxmox_apt_api_types ::{
APTRepositoryFile , APTRepositoryHandle , APTRepositoryInfo , APTStandardRepository ,
2024-07-01 11:32:41 +03:00
} ;
2021-06-23 16:38:54 +03:00
2023-09-05 13:46:20 +03:00
fn create_clean_directory ( path : & PathBuf ) -> Result < ( ) , Error > {
match std ::fs ::remove_dir_all ( path ) {
Err ( err ) if err . kind ( ) = = std ::io ::ErrorKind ::NotFound = > ( ) ,
Err ( err ) = > bail! ( " unable to remove dir {path:?} - {err} " ) ,
Ok ( _ ) = > ( ) ,
}
std ::fs ::create_dir_all ( path )
. map_err ( | err | format_err! ( " unable to create dir {path:?} - {err} " ) )
}
2021-06-23 16:38:54 +03:00
#[ test ]
fn test_parse_write ( ) -> Result < ( ) , Error > {
2021-07-02 12:31:24 +03:00
test_parse_write_dir ( " sources.list.d " ) ? ;
test_parse_write_dir ( " sources.list.d.expected " ) ? ; // check if it's idempotent
Ok ( ( ) )
}
fn test_parse_write_dir ( read_dir : & str ) -> Result < ( ) , Error > {
2021-06-23 16:38:54 +03:00
let test_dir = std ::env ::current_dir ( ) ? . join ( " tests " ) ;
2023-06-02 11:48:22 +03:00
let tmp_dir = PathBuf ::from ( env! ( " CARGO_TARGET_TMPDIR " ) . to_string ( ) ) ;
2021-07-02 12:31:24 +03:00
let read_dir = test_dir . join ( read_dir ) ;
2023-06-02 11:48:22 +03:00
let write_dir = tmp_dir . join ( " sources.list.d.actual " ) ;
2021-06-23 16:38:54 +03:00
let expected_dir = test_dir . join ( " sources.list.d.expected " ) ;
2023-09-05 13:46:20 +03:00
create_clean_directory ( & write_dir ) ? ;
2021-06-23 16:38:54 +03:00
let mut files = vec! [ ] ;
let mut errors = vec! [ ] ;
for entry in std ::fs ::read_dir ( read_dir ) ? {
let path = entry ? . path ( ) ;
match APTRepositoryFile ::new ( & path ) ? {
Some ( mut file ) = > match file . parse ( ) {
Ok ( ( ) ) = > files . push ( file ) ,
Err ( err ) = > errors . push ( err ) ,
} ,
None = > bail! ( " unexpected None for '{:?}' " , path ) ,
}
}
assert! ( errors . is_empty ( ) ) ;
for file in files . iter_mut ( ) {
2022-04-05 16:07:43 +03:00
let path = match & file . path {
Some ( path ) = > path ,
None = > continue ,
} ;
let path = PathBuf ::from ( path ) ;
2021-06-23 16:38:54 +03:00
let new_path = write_dir . join ( path . file_name ( ) . unwrap ( ) ) ;
2022-04-05 16:07:43 +03:00
file . path = Some ( new_path . into_os_string ( ) . into_string ( ) . unwrap ( ) ) ;
2021-06-23 16:38:54 +03:00
file . digest = None ;
file . write ( ) ? ;
}
let mut expected_count = 0 ;
for entry in std ::fs ::read_dir ( expected_dir ) ? {
expected_count + = 1 ;
let expected_path = entry ? . path ( ) ;
2023-06-02 11:48:22 +03:00
2021-06-23 16:38:54 +03:00
let actual_path = write_dir . join ( expected_path . file_name ( ) . unwrap ( ) ) ;
let expected_contents = std ::fs ::read ( & expected_path )
2023-09-05 13:46:20 +03:00
. map_err ( | err | format_err! ( " unable to read {expected_path:?} - {err} " ) ) ? ;
2021-06-23 16:38:54 +03:00
let actual_contents = std ::fs ::read ( & actual_path )
2023-09-05 13:46:20 +03:00
. map_err ( | err | format_err! ( " unable to read {actual_path:?} - {err} " ) ) ? ;
2021-06-23 16:38:54 +03:00
assert_eq! (
expected_contents , actual_contents ,
" Use \n \n diff {:?} {:?} \n \n if you're not fluent in byte decimals " ,
expected_path , actual_path
) ;
}
let actual_count = std ::fs ::read_dir ( write_dir ) ? . count ( ) ;
assert_eq! ( expected_count , actual_count ) ;
Ok ( ( ) )
}
#[ test ]
fn test_digest ( ) -> Result < ( ) , Error > {
let test_dir = std ::env ::current_dir ( ) ? . join ( " tests " ) ;
2023-06-02 11:48:22 +03:00
let tmp_dir = PathBuf ::from ( env! ( " CARGO_TARGET_TMPDIR " ) . to_string ( ) ) ;
2021-06-23 16:38:54 +03:00
let read_dir = test_dir . join ( " sources.list.d " ) ;
2023-06-02 11:48:22 +03:00
let write_dir = tmp_dir . join ( " sources.list.d.digest " ) ;
2021-06-23 16:38:54 +03:00
2023-09-05 13:46:20 +03:00
create_clean_directory ( & write_dir ) ? ;
2021-06-23 16:38:54 +03:00
let path = read_dir . join ( " standard.list " ) ;
let mut file = APTRepositoryFile ::new ( & path ) ? . unwrap ( ) ;
file . parse ( ) ? ;
let new_path = write_dir . join ( path . file_name ( ) . unwrap ( ) ) ;
2022-04-05 16:07:43 +03:00
file . path = Some ( new_path . clone ( ) . into_os_string ( ) . into_string ( ) . unwrap ( ) ) ;
2021-06-23 16:38:54 +03:00
2024-08-07 10:43:48 +03:00
let old_digest = file . digest . unwrap ( ) ;
2021-06-23 16:38:54 +03:00
// file does not exist yet...
assert! ( file . read_with_digest ( ) . is_err ( ) ) ;
assert! ( file . write ( ) . is_err ( ) ) ;
// ...but it should work if there's no digest
file . digest = None ;
file . write ( ) ? ;
// overwrite with old contents...
std ::fs ::copy ( path , new_path ) ? ;
// modify the repo
2024-06-06 12:21:24 +03:00
let repo = file . repositories . first_mut ( ) . unwrap ( ) ;
2021-06-23 16:38:54 +03:00
repo . enabled = ! repo . enabled ;
// ...then it should work
2024-08-07 10:43:48 +03:00
file . digest = Some ( old_digest ) ;
2021-06-23 16:38:54 +03:00
file . write ( ) ? ;
// expect a different digest, because the repo was modified
let ( _ , new_digest ) = file . read_with_digest ( ) ? ;
2024-07-17 10:35:31 +03:00
assert_ne! ( old_digest , * new_digest ) ;
2021-06-23 16:38:54 +03:00
assert! ( file . write ( ) . is_err ( ) ) ;
Ok ( ( ) )
}
#[ test ]
fn test_empty_write ( ) -> Result < ( ) , Error > {
2024-06-06 12:32:19 +03:00
let write_dir = PathBuf ::from (
std ::option_env! ( " CARGO_TARGET_TMPDIR " ) . expect ( " no test target dir set by cargo! " ) ,
)
. join ( " tests " )
. join ( " sources.list.d.remove " ) ;
2021-06-23 16:38:54 +03:00
let test_dir = std ::env ::current_dir ( ) ? . join ( " tests " ) ;
let read_dir = test_dir . join ( " sources.list.d " ) ;
2023-09-05 13:46:20 +03:00
create_clean_directory ( & write_dir ) ? ;
2021-06-23 16:38:54 +03:00
let path = read_dir . join ( " standard.list " ) ;
let mut file = APTRepositoryFile ::new ( & path ) ? . unwrap ( ) ;
file . parse ( ) ? ;
let new_path = write_dir . join ( path . file_name ( ) . unwrap ( ) ) ;
2022-10-12 11:41:42 +03:00
file . path = Some ( new_path . into_os_string ( ) . into_string ( ) . unwrap ( ) ) ;
2021-06-23 16:38:54 +03:00
file . digest = None ;
file . write ( ) ? ;
assert! ( file . exists ( ) ) ;
file . repositories . clear ( ) ;
file . write ( ) ? ;
assert! ( ! file . exists ( ) ) ;
Ok ( ( ) )
}
2021-06-23 16:38:56 +03:00
#[ test ]
fn test_check_repositories ( ) -> Result < ( ) , Error > {
let test_dir = std ::env ::current_dir ( ) ? . join ( " tests " ) ;
let read_dir = test_dir . join ( " sources.list.d " ) ;
2024-07-05 10:54:05 +03:00
let apt_lists_dir : PathBuf = test_dir . join ( " lists " ) ;
2021-06-30 13:20:17 +03:00
2021-06-23 16:38:56 +03:00
let absolute_suite_list = read_dir . join ( " absolute_suite.list " ) ;
2024-06-06 12:21:24 +03:00
let mut file = APTRepositoryFile ::new ( absolute_suite_list ) ? . unwrap ( ) ;
2021-06-23 16:38:56 +03:00
file . parse ( ) ? ;
2024-07-05 10:54:05 +03:00
let infos = check_repositories ( & [ file ] , DebianCodename ::Bullseye , & apt_lists_dir ) ;
2021-06-23 16:38:56 +03:00
2022-10-12 11:41:42 +03:00
assert! ( infos . is_empty ( ) ) ;
2021-06-23 16:38:56 +03:00
let pve_list = read_dir . join ( " pve.list " ) ;
let mut file = APTRepositoryFile ::new ( & pve_list ) ? . unwrap ( ) ;
file . parse ( ) ? ;
let path_string = pve_list . into_os_string ( ) . into_string ( ) . unwrap ( ) ;
2021-06-30 13:20:17 +03:00
let origins = [
" Debian " , " Debian " , " Proxmox " , " Proxmox " , " Proxmox " , " Debian " ,
] ;
2021-06-23 16:38:56 +03:00
let mut expected_infos = vec! [ ] ;
2022-10-12 11:41:42 +03:00
for ( n , origin ) in origins . into_iter ( ) . enumerate ( ) {
2021-06-23 16:38:56 +03:00
expected_infos . push ( APTRepositoryInfo {
path : path_string . clone ( ) ,
index : n ,
2021-06-30 13:20:17 +03:00
property : None ,
kind : " origin " . to_string ( ) ,
2022-10-12 11:41:42 +03:00
message : origin . to_string ( ) ,
2021-06-23 16:38:56 +03:00
} ) ;
}
expected_infos . sort ( ) ;
2024-07-05 10:54:05 +03:00
let mut infos = check_repositories ( & [ file ] , DebianCodename ::Bullseye , & apt_lists_dir ) ;
2021-06-23 16:38:56 +03:00
infos . sort ( ) ;
assert_eq! ( infos , expected_infos ) ;
let bad_sources = read_dir . join ( " bad.sources " ) ;
let mut file = APTRepositoryFile ::new ( & bad_sources ) ? . unwrap ( ) ;
file . parse ( ) ? ;
let path_string = bad_sources . into_os_string ( ) . into_string ( ) . unwrap ( ) ;
let mut expected_infos = vec! [
APTRepositoryInfo {
path : path_string . clone ( ) ,
index : 0 ,
property : Some ( " Suites " . to_string ( ) ) ,
kind : " warning " . to_string ( ) ,
message : " suite 'sid' should not be used in production! " . to_string ( ) ,
} ,
APTRepositoryInfo {
path : path_string . clone ( ) ,
index : 1 ,
property : Some ( " Suites " . to_string ( ) ) ,
kind : " warning " . to_string ( ) ,
message : " old suite 'lenny' configured! " . to_string ( ) ,
} ,
APTRepositoryInfo {
path : path_string . clone ( ) ,
index : 2 ,
property : Some ( " Suites " . to_string ( ) ) ,
kind : " warning " . to_string ( ) ,
message : " old suite 'stretch' configured! " . to_string ( ) ,
} ,
APTRepositoryInfo {
path : path_string . clone ( ) ,
index : 3 ,
property : Some ( " Suites " . to_string ( ) ) ,
kind : " warning " . to_string ( ) ,
message : " use the name of the stable distribution instead of 'stable'! " . to_string ( ) ,
} ,
APTRepositoryInfo {
path : path_string . clone ( ) ,
index : 4 ,
property : Some ( " Suites " . to_string ( ) ) ,
kind : " ignore-pre-upgrade-warning " . to_string ( ) ,
message : " suite 'bookworm' should not be used in production! " . to_string ( ) ,
} ,
APTRepositoryInfo {
path : path_string . clone ( ) ,
index : 5 ,
property : Some ( " Suites " . to_string ( ) ) ,
kind : " warning " . to_string ( ) ,
message : " suite 'testing' should not be used in production! " . to_string ( ) ,
} ,
] ;
for n in 0 ..= 5 {
expected_infos . push ( APTRepositoryInfo {
path : path_string . clone ( ) ,
index : n ,
2021-06-30 13:20:17 +03:00
property : None ,
kind : " origin " . to_string ( ) ,
message : " Debian " . to_string ( ) ,
2021-06-23 16:38:56 +03:00
} ) ;
}
expected_infos . sort ( ) ;
2024-07-05 10:54:05 +03:00
let mut infos = check_repositories ( & [ file ] , DebianCodename ::Bullseye , & apt_lists_dir ) ;
2021-06-23 16:38:56 +03:00
infos . sort ( ) ;
assert_eq! ( infos , expected_infos ) ;
2022-01-18 15:48:22 +03:00
let bad_security = read_dir . join ( " bad-security.list " ) ;
let mut file = APTRepositoryFile ::new ( & bad_security ) ? . unwrap ( ) ;
file . parse ( ) ? ;
let path_string = bad_security . into_os_string ( ) . into_string ( ) . unwrap ( ) ;
let mut expected_infos = vec! [ ] ;
for n in 0 ..= 1 {
expected_infos . push ( APTRepositoryInfo {
path : path_string . clone ( ) ,
index : n ,
property : Some ( " Suites " . to_string ( ) ) ,
kind : " warning " . to_string ( ) ,
message : " expected suite 'bullseye-security' " . to_string ( ) ,
} ) ;
}
for n in 0 ..= 1 {
expected_infos . push ( APTRepositoryInfo {
path : path_string . clone ( ) ,
index : n ,
property : None ,
kind : " origin " . to_string ( ) ,
message : " Debian " . to_string ( ) ,
} ) ;
}
expected_infos . sort ( ) ;
2024-07-05 10:54:05 +03:00
let mut infos = check_repositories ( & [ file ] , DebianCodename ::Bullseye , & apt_lists_dir ) ;
2022-01-18 15:48:22 +03:00
infos . sort ( ) ;
assert_eq! ( infos , expected_infos ) ;
2021-06-23 16:38:56 +03:00
Ok ( ( ) )
}
2021-06-30 13:20:16 +03:00
#[ test ]
fn test_get_cached_origin ( ) -> Result < ( ) , Error > {
let test_dir = std ::env ::current_dir ( ) ? . join ( " tests " ) ;
let read_dir = test_dir . join ( " sources.list.d " ) ;
2024-07-05 10:54:05 +03:00
let apt_lists_dir : PathBuf = test_dir . clone ( ) . join ( " lists " ) ;
2021-06-30 13:20:16 +03:00
let pve_list = read_dir . join ( " pve.list " ) ;
2024-06-06 12:21:24 +03:00
let mut file = APTRepositoryFile ::new ( pve_list ) ? . unwrap ( ) ;
2021-06-30 13:20:16 +03:00
file . parse ( ) ? ;
let origins = [
Some ( " Debian " . to_string ( ) ) ,
Some ( " Debian " . to_string ( ) ) ,
Some ( " Proxmox " . to_string ( ) ) ,
None , // no cache file exists
None , // no cache file exists
Some ( " Debian " . to_string ( ) ) ,
] ;
assert_eq! ( file . repositories . len ( ) , origins . len ( ) ) ;
for ( n , repo ) in file . repositories . iter ( ) . enumerate ( ) {
2024-07-05 10:54:05 +03:00
assert_eq! ( repo . get_cached_origin ( & apt_lists_dir ) ? , origins [ n ] ) ;
2021-06-30 13:20:16 +03:00
}
Ok ( ( ) )
}
2021-06-23 16:38:57 +03:00
#[ test ]
fn test_standard_repositories ( ) -> Result < ( ) , Error > {
let test_dir = std ::env ::current_dir ( ) ? . join ( " tests " ) ;
let read_dir = test_dir . join ( " sources.list.d " ) ;
let mut expected = vec! [
2024-07-01 12:22:05 +03:00
APTStandardRepository ::from_handle ( APTRepositoryHandle ::Enterprise ) ,
APTStandardRepository ::from_handle ( APTRepositoryHandle ::NoSubscription ) ,
APTStandardRepository ::from_handle ( APTRepositoryHandle ::Test ) ,
APTStandardRepository ::from_handle ( APTRepositoryHandle ::CephQuincyEnterprise ) ,
APTStandardRepository ::from_handle ( APTRepositoryHandle ::CephQuincyNoSubscription ) ,
APTStandardRepository ::from_handle ( APTRepositoryHandle ::CephQuincyTest ) ,
APTStandardRepository ::from_handle ( APTRepositoryHandle ::CephReefEnterprise ) ,
APTStandardRepository ::from_handle ( APTRepositoryHandle ::CephReefNoSubscription ) ,
APTStandardRepository ::from_handle ( APTRepositoryHandle ::CephReefTest ) ,
2021-06-23 16:38:57 +03:00
] ;
let absolute_suite_list = read_dir . join ( " absolute_suite.list " ) ;
2024-06-06 12:21:24 +03:00
let mut file = APTRepositoryFile ::new ( absolute_suite_list ) ? . unwrap ( ) ;
2021-06-23 16:38:57 +03:00
file . parse ( ) ? ;
2022-10-12 11:41:42 +03:00
let std_repos = standard_repositories ( & [ file ] , " pve " , DebianCodename ::Bullseye ) ;
2021-06-23 16:38:57 +03:00
2023-09-04 18:14:19 +03:00
assert_eq! ( std_repos , & expected [ 0 ..= 5 ] ) ;
let absolute_suite_list = read_dir . join ( " absolute_suite.list " ) ;
2024-06-06 12:21:24 +03:00
let mut file = APTRepositoryFile ::new ( absolute_suite_list ) ? . unwrap ( ) ;
2023-09-04 18:14:19 +03:00
file . parse ( ) ? ;
let std_repos = standard_repositories ( & [ file ] , " pve " , DebianCodename ::Bookworm ) ;
2021-06-23 16:38:57 +03:00
assert_eq! ( std_repos , expected ) ;
let pve_list = read_dir . join ( " pve.list " ) ;
2024-06-06 12:21:24 +03:00
let mut file = APTRepositoryFile ::new ( pve_list ) ? . unwrap ( ) ;
2021-06-23 16:38:57 +03:00
file . parse ( ) ? ;
let file_vec = vec! [ file ] ;
2021-07-29 15:25:52 +03:00
let std_repos = standard_repositories ( & file_vec , " pbs " , DebianCodename ::Bullseye ) ;
2021-06-23 16:38:57 +03:00
assert_eq! ( & std_repos , & expected [ 0 ..= 2 ] ) ;
expected [ 0 ] . status = Some ( false ) ;
expected [ 1 ] . status = Some ( true ) ;
2021-07-29 15:25:52 +03:00
let std_repos = standard_repositories ( & file_vec , " pve " , DebianCodename ::Bullseye ) ;
2021-06-23 16:38:57 +03:00
2023-09-04 18:14:19 +03:00
assert_eq! ( std_repos , & expected [ 0 ..= 5 ] ) ;
2021-06-23 16:38:57 +03:00
2021-07-16 15:36:02 +03:00
let pve_alt_list = read_dir . join ( " pve-alt.list " ) ;
2024-06-06 12:21:24 +03:00
let mut file = APTRepositoryFile ::new ( pve_alt_list ) ? . unwrap ( ) ;
2021-07-16 15:36:02 +03:00
file . parse ( ) ? ;
expected [ 0 ] . status = Some ( true ) ;
expected [ 1 ] . status = Some ( true ) ;
expected [ 2 ] . status = Some ( false ) ;
2023-06-02 11:48:23 +03:00
let std_repos = standard_repositories ( & [ file ] , " pve " , DebianCodename ::Bullseye ) ;
2021-07-16 15:36:02 +03:00
2023-09-04 18:14:19 +03:00
assert_eq! ( std_repos , & expected [ 0 ..= 5 ] ) ;
2021-07-16 15:36:02 +03:00
2023-06-02 11:48:24 +03:00
let pve_alt_list = read_dir . join ( " ceph-quincy-bookworm.list " ) ;
2024-06-06 12:21:24 +03:00
let mut file = APTRepositoryFile ::new ( pve_alt_list ) ? . unwrap ( ) ;
2023-06-02 11:48:24 +03:00
file . parse ( ) ? ;
expected [ 0 ] . status = None ;
expected [ 1 ] . status = None ;
expected [ 2 ] . status = None ;
expected [ 3 ] . status = Some ( true ) ;
expected [ 4 ] . status = Some ( true ) ;
expected [ 5 ] . status = Some ( true ) ;
let std_repos = standard_repositories ( & [ file ] , " pve " , DebianCodename ::Bookworm ) ;
assert_eq! ( std_repos , expected ) ;
let pve_alt_list = read_dir . join ( " ceph-quincy-nosub-bookworm.list " ) ;
2024-06-06 12:21:24 +03:00
let mut file = APTRepositoryFile ::new ( pve_alt_list ) ? . unwrap ( ) ;
2023-06-02 11:48:24 +03:00
file . parse ( ) ? ;
expected [ 0 ] . status = None ;
expected [ 1 ] . status = None ;
expected [ 2 ] . status = None ;
expected [ 3 ] . status = None ;
expected [ 4 ] . status = Some ( true ) ;
expected [ 5 ] . status = None ;
let std_repos = standard_repositories ( & [ file ] , " pve " , DebianCodename ::Bookworm ) ;
assert_eq! ( std_repos , expected ) ;
2023-09-04 18:14:19 +03:00
let pve_alt_list = read_dir . join ( " ceph-reef-enterprise-bookworm.list " ) ;
2024-06-06 12:21:24 +03:00
let mut file = APTRepositoryFile ::new ( pve_alt_list ) ? . unwrap ( ) ;
2023-09-04 18:14:19 +03:00
file . parse ( ) ? ;
expected [ 0 ] . status = None ;
expected [ 1 ] . status = None ;
expected [ 2 ] . status = None ;
expected [ 3 ] . status = None ;
expected [ 4 ] . status = None ;
expected [ 5 ] . status = None ;
expected [ 6 ] . status = Some ( true ) ;
expected [ 7 ] . status = None ;
expected [ 8 ] . status = None ;
let std_repos = standard_repositories ( & [ file ] , " pve " , DebianCodename ::Bookworm ) ;
assert_eq! ( std_repos , expected ) ;
2021-06-23 16:38:57 +03:00
Ok ( ( ) )
}
2021-07-29 15:25:48 +03:00
#[ test ]
fn test_get_current_release_codename ( ) -> Result < ( ) , Error > {
let codename = get_current_release_codename ( ) ? ;
2023-05-24 10:50:24 +03:00
assert! ( codename = = DebianCodename ::Bookworm ) ;
2021-07-29 15:25:48 +03:00
Ok ( ( ) )
}