From 068eea55e0612184151461e64633b3dc18e53490 Mon Sep 17 00:00:00 2001 From: Namilskyy Date: Mon, 1 Dec 2025 14:06:10 +0300 Subject: Implemented test suite, fixed some issues and added more modular structure into .woodpecker.yaml --- .woodpecker.yaml | 52 ++++++++- Cargo.lock | 255 ++++++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 7 ++ src/cfg/config.rs | 10 +- src/lib.rs | 14 +++ src/main.rs | 2 +- src/net/http_package.rs | 43 +++++--- src/net/i2p_package.rs | 4 +- src/pkgtoolkit/pkgtools.rs | 2 +- tests/http_funcs.rs | 199 +++++++++++++++++++++++++++++++++++ tests/i2p_functions.rs | 101 ++++++++++++++++++ tests/pkgtoolkit_funcs.rs | 193 ++++++++++++++++++++++++++++++++++ tests/secondary_funcs.rs | 29 ++++++ tests/shared.rs | 22 ++++ 14 files changed, 894 insertions(+), 39 deletions(-) create mode 100644 src/lib.rs create mode 100644 tests/http_funcs.rs create mode 100644 tests/i2p_functions.rs create mode 100644 tests/pkgtoolkit_funcs.rs create mode 100644 tests/secondary_funcs.rs create mode 100644 tests/shared.rs diff --git a/.woodpecker.yaml b/.woodpecker.yaml index 46a5e75..ea55210 100644 --- a/.woodpecker.yaml +++ b/.woodpecker.yaml @@ -1,16 +1,58 @@ # Define the sequence of steps for the CI pipeline steps: - full-test: + dependencies: image: rust - commands: - - apt install openssl -y + environment: + RUST_BACKTRACE: 1 + CARGO_TERM_COLOR: always + commands: - rustup default stable - rustup component add clippy rustfmt - - cargo clippy --jobs 2 -- -D clippy::all + - apt install openssl -y + when: + branch: main + event: [ push, pull_request ] + build: + image: rust + environment: + RUST_BACKTRACE: 1 + CARGO_TERM_COLOR: always + commands: + - cargo build --verbose --release --jobs 2 + when: + branch: main + event: [ push, pull_request ] + clippy: + image: rust + environment: + RUST_BACKTRACE: 1 + CARGO_TERM_COLOR: always + commands: - cargo fmt --all -- --check + - cargo clippy --jobs 2 -- -D clippy::all + when: + branch: main + event: [ push, pull_request ] + + build: + image: rust + environment: + RUST_BACKTRACE: 1 + CARGO_TERM_COLOR: always + commands: - cargo build --verbose --release --jobs 2 - - cargo test --verbose --jobs 2 when: branch: main event: [ push, pull_request ] + + tests: + image: rust + environment: + RUST_BACKTRACE: 1 + CARGO_TERM_COLOR: always + commands: + - cargo test --verbose --jobs 2 + when: + branch: main + event: [ push, pull_request ] \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 5716b77..bcfa62f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,15 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -85,6 +94,38 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -254,6 +295,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "colored" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "console" version = "0.16.1" @@ -320,7 +370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -457,7 +507,7 @@ checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ "curve25519-dalek", "ed25519", - "rand_core", + "rand_core 0.6.4", "serde", "sha2", "subtle", @@ -477,7 +527,7 @@ dependencies = [ "generic-array", "group", "pkcs8", - "rand_core", + "rand_core 0.6.4", "sec1", "subtle", "zeroize", @@ -509,7 +559,7 @@ dependencies = [ "num-traits", "p256", "parking_lot", - "rand_core", + "rand_core 0.6.4", "sha1", "sha2", "siphasher", @@ -536,6 +586,29 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -575,7 +648,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -788,7 +861,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -888,6 +961,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hyper" version = "1.8.1" @@ -902,6 +981,7 @@ dependencies = [ "http", "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "pin-utils", @@ -1130,6 +1210,30 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jiff" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "js-sys" version = "0.3.83" @@ -1203,15 +1307,19 @@ dependencies = [ "cc", "clap", "emissary-core", + "env_logger", "flate2", "futures-util", "indicatif", "log", + "mockito", "reqwest", "serde", "sqlite", "tar", + "tempfile", "tokio", + "tokio-test", "toml", "url", "yosemite", @@ -1250,6 +1358,31 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "mockito" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e0603425789b4a70fcc4ac4f5a46a566c116ee3e2a6b768dc623f7719c611de" +dependencies = [ + "assert-json-diff", + "bytes", + "colored", + "futures-core", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "log", + "pin-project-lite", + "rand 0.9.2", + "regex", + "serde_json", + "serde_urlencoded", + "similar", + "tokio", +] + [[package]] name = "native-tls" version = "0.2.14" @@ -1473,6 +1606,15 @@ version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -1531,8 +1673,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -1542,7 +1694,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -1554,6 +1716,15 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -1563,6 +1734,35 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + [[package]] name = "reqwest" version = "0.12.24" @@ -1854,7 +2054,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -1863,6 +2063,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + [[package]] name = "siphasher" version = "1.0.1" @@ -2086,6 +2292,7 @@ dependencies = [ "bytes", "libc", "mio", + "parking_lot", "pin-project-lite", "socket2", "tokio-macros", @@ -2123,6 +2330,30 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-test" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468baabc3311435b55dd935f702f42cd1b8abb7e754fb7dfb16bd36aa88f9f7" +dependencies = [ + "async-stream", + "bytes", + "futures-core", + "tokio", + "tokio-stream", +] + [[package]] name = "tokio-util" version = "0.7.17" @@ -2677,7 +2908,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", - "rand_core", + "rand_core 0.6.4", "zeroize", ] @@ -2722,7 +2953,7 @@ checksum = "19cf621ae2b2d7e5d1e3827231fbf59ca4fb72b3589e36cdf648e820f1cf6e0d" dependencies = [ "futures", "nom", - "rand", + "rand 0.8.5", "thiserror", "tokio", "tracing", diff --git a/Cargo.toml b/Cargo.toml index 5cdd184..0422956 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,10 @@ url = "2.5.7" indicatif = "0.18.3" futures-util = "0.3.31" +[dev-dependencies] +env_logger = "0.11.8" +log = "0.4.28" +mockito = "1.7.1" +tempfile = "3.23.0" +tokio-test = "0.4.4" + diff --git a/src/cfg/config.rs b/src/cfg/config.rs index de9901f..213224f 100644 --- a/src/cfg/config.rs +++ b/src/cfg/config.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use std::fs; use toml; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Deserialize, Debug, Serialize, Clone)] #[serde(rename_all = "lowercase")] pub enum Loglevel { Trace, @@ -13,14 +13,14 @@ pub enum Loglevel { } /// `mesk.toml` configuration fields here -#[derive(Deserialize, Debug, Serialize)] +#[derive(Deserialize, Debug, Serialize, Clone)] pub struct Config { pub repo: Repo, pub log: Log, pub paths: Paths, } -#[derive(Deserialize, Debug, Serialize)] +#[derive(Deserialize, Debug, Serialize, Clone)] pub struct Log { #[serde(rename = "log_file")] pub log_file: String, @@ -29,7 +29,7 @@ pub struct Log { } // Rename needed for editing mesk.toml file fields but dont touch code. -#[derive(Deserialize, Debug, Serialize)] +#[derive(Deserialize, Debug, Serialize, Clone)] pub struct Repo { #[serde(rename = "repo_url")] pub repo_url: String, @@ -43,7 +43,7 @@ pub struct Repo { // pub arch = arch; } -#[derive(Deserialize, Debug, Serialize)] +#[derive(Deserialize, Debug, Serialize, Clone)] pub struct Paths { #[serde(rename = "cache_dir")] pub cache_dir: String, diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..ed6b426 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,14 @@ + +pub mod cfg { + pub mod config; +} + +pub mod net { + pub mod http_package; + pub mod i2p_package; +} + +pub mod pkgtoolkit { + pub mod pkgtools; +} + diff --git a/src/main.rs b/src/main.rs index 2d36f20..4403c51 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ mod cfg; -mod net; // This should contain both i2p_package and http_package modules +mod net; mod pkgtoolkit; use crate::cfg::config::Config; diff --git a/src/net/http_package.rs b/src/net/http_package.rs index ec7c318..ade4ee1 100644 --- a/src/net/http_package.rs +++ b/src/net/http_package.rs @@ -4,14 +4,15 @@ use futures_util::stream::TryStreamExt; use indicatif::{ProgressBar, ProgressStyle}; use reqwest; use serde::Deserialize; -use std::collections::HashMap; -use std::path::Path; -use tokio::fs::File; -use tokio::io::AsyncWriteExt; +use std::{ collections::HashMap, path::Path }; +use tokio::{ fs::File, io::AsyncWriteExt}; +use std::fs::File as StdFile; +use flate2::read::GzDecoder; +use tar::Archive; pub struct HTTPPackage { - config: Config, - index_packages: Option>, + pub config: Config, + pub index_packages: Option>, } #[derive(Deserialize, Debug)] @@ -42,6 +43,8 @@ impl HTTPPackage { let repo_url_str = &self.config.repo.repo_url; let cache_dir = &self.config.paths.cache_dir; + log::debug!("Cache directory: {:?}", cache_dir); + let index_url = if repo_url_str.ends_with(".tar.gz") { repo_url_str.clone() } else { @@ -86,6 +89,10 @@ impl HTTPPackage { let mut stream = response.bytes_stream(); let file_path = Path::new(cache_dir).join("INDEX.tar.gz"); + // Ensure cache_dir exists + tokio::fs::create_dir_all(cache_dir).await + .map_err(|e| format!("Failed to create cache dir: {}", e))?; + let mut file = File::create(&file_path).await?; let mut downloaded: u64 = 0; @@ -98,9 +105,16 @@ impl HTTPPackage { pb.finish_with_message("INDEX.tar.gz download finished"); - // --- Извлечение и парсинг INDEX.toml --- log::info!("Extracting INDEX.tar.gz to cache directory..."); - Package::extract_archive(&file_path.to_string_lossy())?; // Используем существующую функцию из pkgtoolkit + // Package::extract_archive(&file_path.to_string_lossy())?; + + + let archive_file = StdFile::open(&file_path) + .map_err(|e| format!("Failed to open archive: {}", e))?; + let gz_decoder = GzDecoder::new(archive_file); + let mut archive = Archive::new(gz_decoder); + archive.unpack(cache_dir) + .map_err(|e| format!("Failed to unpack archive: {}", e))?; let index_toml_path = Path::new(cache_dir).join("INDEX.toml"); if !index_toml_path.exists() { @@ -110,12 +124,13 @@ impl HTTPPackage { } let index_content = tokio::fs::read_to_string(&index_toml_path).await?; - let index_data: IndexData = toml::from_str(&index_content)?; + log::debug!("Content of INDEX.toml:\n{}", index_content); + + let index_data: IndexData = toml::from_str(&index_content) + .map_err(|e| format!("Failed to parse INDEX.toml: {}", e))?; let mut package_map = HashMap::new(); for pkg in index_data.packages { - // PKG_URL = /repo/package.mesk - // FULL URL = "http://mesk.anthrill.i2p/i2p/repo/pkg.mesk" let base_url = url::Url::parse(&self.config.repo.repo_url)?; let full_url = base_url.join(&pkg.url)?; let mut pkg_clone = pkg.clone(); @@ -131,7 +146,7 @@ impl HTTPPackage { ); Ok(true) - } +} /// An internal auxiliary function for downloading data and writing it to a file with a progress display. /// @@ -212,7 +227,7 @@ impl HTTPPackage { ) -> Result> { let package_info = self.fetch_package_info(package_name)?; let url = &package_info.url; - + let client = reqwest::Client::new(); let file_name = Path::new(url) @@ -223,6 +238,8 @@ impl HTTPPackage { let cache_dir = &self.config.paths.cache_dir; let file_path = Path::new(cache_dir).join(file_name); + tokio::fs::create_dir_all(&cache_dir).await?; + Self::download_file_with_progress(&client, url, &file_path, file_name).await?; log::info!( diff --git a/src/net/i2p_package.rs b/src/net/i2p_package.rs index 751d561..2b847d6 100644 --- a/src/net/i2p_package.rs +++ b/src/net/i2p_package.rs @@ -19,8 +19,8 @@ use yosemite::Session; use yosemite::SessionOptions; pub struct I2PPackage { - config: Config, - index_packages: Option>, + pub config: Config, + pub index_packages: Option>, } #[derive(Deserialize, Debug)] diff --git a/src/pkgtoolkit/pkgtools.rs b/src/pkgtoolkit/pkgtools.rs index 9fe01c6..29145d3 100644 --- a/src/pkgtoolkit/pkgtools.rs +++ b/src/pkgtoolkit/pkgtools.rs @@ -77,7 +77,7 @@ struct Build { } impl Archs { - fn as_str(&self) -> &'static str { + pub fn as_str(&self) -> &'static str { match self { Archs::X86_64 => "x86_64", Archs::Aarch64 => "aarch64", diff --git a/tests/http_funcs.rs b/tests/http_funcs.rs new file mode 100644 index 0000000..efbaa53 --- /dev/null +++ b/tests/http_funcs.rs @@ -0,0 +1,199 @@ +use mesk::pkgtoolkit::pkgtools::{Package, Archs}; +use mesk::net::http_package::HTTPPackage; +use std::fs; + +mod shared; +use shared::create_test_config; + +use std::collections::HashMap; +use tempfile::TempDir; +use tokio; + +// Add these imports for logging +use log::{info, debug, error}; + +#[cfg(test)] +mod http_package_tests { + use super::*; + + // Helper function to initialize the logger for each test. + // This uses `call_once` to ensure it's only initialized once, + // even if called multiple times, preventing panics. + fn init_logger() { + let _ = env_logger::builder() + .is_test(true) + .try_init(); + } + + #[tokio::test] + async fn test_http_fetch_index_success() -> Result<(), Box> { + init_logger(); // Initialize logger + info!("Starting test_http_fetch_index_success"); + + let mut server = mockito::Server::new_async().await; + let index_toml_content = r#" +[[packages]] +name = "test-pkg" +version = "1.0.0" +arch = "X86_64" +url = "/repo/test-pkg.mesk" +descr = "A test package from index" +license = "MIT" +"#; + + let temp_dir = TempDir::new()?; + let index_toml_path = temp_dir.path().join("INDEX.toml"); + fs::write(&index_toml_path, index_toml_content)?; + let archive_path = temp_dir.path().join("INDEX.tar.gz"); + + // Создаём архив + let file = fs::File::create(&archive_path)?; + let gz_encoder = flate2::write::GzEncoder::new(file, flate2::Compression::default()); + let mut tar_builder = tar::Builder::new(gz_encoder); + tar_builder.append_path_with_name(&index_toml_path, "INDEX.toml")?; + tar_builder.into_inner()?.finish()?; + + let mock_body = fs::read(&archive_path)?; + let _mock = server + .mock("GET", "/INDEX.tar.gz") + .with_status(200) + .with_header("content-length", &mock_body.len().to_string()) + .with_body(mock_body) + .create_async() + .await; + + let temp_config_dir = TempDir::new()?; + let config = create_test_config(temp_config_dir.path().to_str().unwrap()); + + let mut config_with_mock_url = config.clone(); + config_with_mock_url.repo.repo_url = server.url(); + + let mut http_pkg = HTTPPackage::new(config_with_mock_url); + + debug!("Attempting to fetch index via HTTP"); + let result = http_pkg.fetch_index_http().await; + + info!("Checking if fetch_index_http result is OK"); + assert!(result.is_ok(), "fetch_index_http should return Ok"); + + info!("Checking if fetch_index_http returned true"); + assert_eq!(result.unwrap(), true, "fetch_index_http should indicate success"); + + + info!("Checking if index_packages is populated"); + assert!(http_pkg.index_packages.is_some(), "index_packages should not be None after successful fetch"); + + let packages = http_pkg.index_packages.as_ref().unwrap(); + info!("Checking number of packages in the index"); + assert_eq!(packages.len(), 1, "There should be exactly one package in the index"); + + info!("Checking if 'test-pkg' exists in the index"); + assert!(packages.contains_key("test-pkg"), "The package 'test-pkg' should be present in the index"); + + let pkg_info = packages.get("test-pkg").unwrap(); + + info!("Checking if package URL is correctly formed"); + assert!(pkg_info.url.starts_with(&server.url()), "Package URL should be prefixed with the server URL"); + + Ok(()) + } + + #[tokio::test] + async fn test_http_fetch_index_http_error() -> Result<(), Box> { + init_logger(); + info!("Starting test_http_fetch_index_http_error"); + + let mut server = mockito::Server::new_async().await; + let _mock = server + .mock("GET", "/INDEX.tar.gz") + .with_status(404) // Ошибка HTTP + .create_async() + .await; + + let temp_config_dir = TempDir::new()?; + let config = create_test_config(temp_config_dir.path().to_str().unwrap()); + let mut config_with_mock_url = config.clone(); + config_with_mock_url.repo.repo_url = server.url(); + + let mut http_pkg = HTTPPackage::new(config_with_mock_url); + + debug!("Attempting to fetch index with expected 404 error"); + let result = http_pkg.fetch_index_http().await; + + info!("Checking if fetch_index_http resulted in an error as expected"); + assert!(result.is_err(), "fetch_index_http should return an Err for a 404 response"); + + let err_msg = result.unwrap_err().to_string(); + info!("Verifying error message contains 'HTTP Error: 404'"); + assert!(err_msg.contains("HTTP Error: 404"), "Error message should contain 'HTTP Error: 404', got: {}", err_msg); + + Ok(()) + } + + #[tokio::test] + async fn test_http_fetch_package_info_success() -> Result<(), Box> { + init_logger(); + info!("Starting test_http_fetch_package_info_success"); + + let temp_config_dir = TempDir::new()?; + let config = create_test_config(temp_config_dir.path().to_str().unwrap()); + + let mut http_pkg = HTTPPackage::new(config); + let mut packages_map = HashMap::new(); + let pkg = Package { + name: "test-pkg".to_string(), + version: "1.0.0".to_string(), + arch: Archs::X86_64, + descr: Some("Test".to_string()), + license: Some("MIT".to_string()), + url: "http://example.com/repo/test-pkg.mesk".to_string(), + }; + packages_map.insert("test-pkg".to_string(), pkg); + http_pkg.index_packages = Some(packages_map); + + debug!("Fetching package info for 'test-pkg'"); + let pkg_info = http_pkg.fetch_package_info("test-pkg"); + + info!("Checking if fetch_package_info returned successfully"); + assert!(pkg_info.is_ok(), "fetch_package_info should return Ok for an existing package"); + + info!("Checking the retrieved package name"); + assert_eq!(pkg_info.unwrap().name, "test-pkg", "Retrieved package name should be 'test-pkg'"); + + Ok(()) + } + + #[tokio::test] + async fn test_http_fetch_package_info_not_found() -> Result<(), Box> { + init_logger(); + info!("Starting test_http_fetch_package_info_not_found"); + + let temp_config_dir = TempDir::new()?; + let config = create_test_config(temp_config_dir.path().to_str().unwrap()); + + let http_pkg = HTTPPackage::new(config.clone()); + debug!("Attempting to fetch package info before index load"); + let result = http_pkg.fetch_package_info("nonexistent-pkg"); + + info!("Checking if fetch_package_info failed due to unloaded index"); + assert!(result.is_err(), "fetch_package_info should fail when index is not loaded"); + + let err_msg = result.unwrap_err().to_string(); + info!("Verifying error message contains 'Index not loaded'"); + assert!(err_msg.contains("Index not loaded"), "Error message should contain 'Index not loaded', got: {}", err_msg); + + let mut http_pkg_empty_index = HTTPPackage::new(config); + http_pkg_empty_index.index_packages = Some(HashMap::new()); + debug!("Attempting to fetch package info from an empty index"); + let result_empty = http_pkg_empty_index.fetch_package_info("nonexistent-pkg"); + + info!("Checking if fetch_package_info failed due to missing package in index"); + assert!(result_empty.is_err(), "fetch_package_info should fail for a package not in the index"); + + let err_msg_empty = result_empty.unwrap_err().to_string(); + info!("Verifying error message contains 'not found in index'"); + assert!(err_msg_empty.contains("not found in index"), "Error message should contain 'not found in index', got: {}", err_msg_empty); + + Ok(()) + } +} \ No newline at end of file diff --git a/tests/i2p_functions.rs b/tests/i2p_functions.rs new file mode 100644 index 0000000..6b24d14 --- /dev/null +++ b/tests/i2p_functions.rs @@ -0,0 +1,101 @@ +use mesk::pkgtoolkit::pkgtools::{Package, Archs}; +use mesk::net::i2p_package::I2PPackage; + +mod shared; +use shared::create_test_config; + +use std::collections::HashMap; +use tempfile::TempDir; +use tokio; + +#[cfg(test)] +mod i2p_package_tests { + use super::*; + + // fn parse_http_response(response_bytes: &[u8]) -> Result<(u16, &[u8]), Box> { ... } + // #[test] + // fn test_parse_http_response() { ... } + /* + #[tokio::test] + async fn test_i2p_fetch_index_success() -> Result<(), Box> { + let temp_config_dir = TempDir::new()?; + let config = create_test_config(temp_config_dir.path().to_str().unwrap()); + let mut config_with_mock_url = config.clone(); + config_with_mock_url.repo.repo_url = "http://dummy.i2p/repo"; + + let mut mock_session = MockSession::new(); + mock_session + .expect_connect() + .with(eq("dummy.i2p")) + .returning(|_| { + // Возвращаем мок-поток, который возвращает байты архива + let index_toml_content = r#"[[packages]] name = "test-pkg" ... "#; + let mut archive_data = Vec::new(); + // ... заполняем archive_data как в test_http_fetch_index_success ... + let response_body = format!( + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}", + archive_data.len(), + std::str::from_utf8(&archive_data).unwrap() + ).into_bytes(); + Ok(MockStream::new(response_body)) + }); + + + let mut i2p_pkg = I2PPackage::new_with_session(config_with_mock_url, Box::new(mock_session)); + + let result = i2p_pkg.fetch_index().await; + + assert!(result.is_ok()); + + Ok(()) + } + */ + #[tokio::test] + async fn test_i2p_fetch_package_info_success() -> Result<(), Box> { + let temp_config_dir = TempDir::new()?; + let config = create_test_config(temp_config_dir.path().to_str().unwrap()); + + let mut i2p_pkg = I2PPackage::new(config); + let mut packages_map = HashMap::new(); + let pkg = Package { + name: "test-pkg".to_string(), + version: "1.0.0".to_string(), + arch: Archs::X86_64, + descr: Some("Test".to_string()), + license: Some("MIT".to_string()), + url: "http://example.i2p/repo/test-pkg.mesk".to_string(), + }; + packages_map.insert("test-pkg".to_string(), pkg); + i2p_pkg.index_packages = Some(packages_map); + + let pkg_info = i2p_pkg.fetch_package_info("test-pkg"); + + assert!(pkg_info.is_ok()); + assert_eq!(pkg_info.unwrap().name, "test-pkg"); + + Ok(()) + } + + #[tokio::test] + async fn test_i2p_fetch_package_info_not_found() -> Result<(), Box> { + let temp_config_dir = TempDir::new()?; + let config = create_test_config(temp_config_dir.path().to_str().unwrap()); + + let i2p_pkg = I2PPackage::new(config.clone()); // index_packages = None + let result = i2p_pkg.fetch_package_info("nonexistent-pkg"); + + assert!(result.is_err()); + let err_msg = result.unwrap_err().to_string(); + assert!(err_msg.contains("Index not loaded")); + + let mut i2p_pkg_empty_index = I2PPackage::new(config); + i2p_pkg_empty_index.index_packages = Some(HashMap::new()); + let result_empty = i2p_pkg_empty_index.fetch_package_info("nonexistent-pkg"); + + assert!(result_empty.is_err()); + let err_msg_empty = result_empty.unwrap_err().to_string(); + assert!(err_msg_empty.contains("not found in index")); + + Ok(()) + } +} diff --git a/tests/pkgtoolkit_funcs.rs b/tests/pkgtoolkit_funcs.rs new file mode 100644 index 0000000..ae0c4d7 --- /dev/null +++ b/tests/pkgtoolkit_funcs.rs @@ -0,0 +1,193 @@ +use mesk::pkgtoolkit::pkgtools::{Package, Archs}; + + +mod shared; +use shared::create_test_config; + +use tempfile::TempDir; +use tokio; + + +// Pkg toolkit tests +#[cfg(test)] +mod package_tests { + use super::*; + use std::fs; + use std::io::Write; + + #[test] + fn test_archs_as_str() { + assert_eq!(Archs::X86_64.as_str(), "x86_64"); + assert_eq!(Archs::Aarch64.as_str(), "aarch64"); + assert_eq!(Archs::X86.as_str(), "x86"); + assert_eq!(Archs::ArmV7.as_str(), "armv7"); + assert_eq!(Archs::ArmV8.as_str(), "armv8"); + } + + #[tokio::test] + async fn test_extract_archive() -> Result<(), Box> { + let temp_dir = TempDir::new()?; + let _config = create_test_config(temp_dir.path().to_str().unwrap()); + + + let test_file_content = "test content"; + let test_file_path = temp_dir.path().join("test_file.txt"); + fs::write(&test_file_path, test_file_content)?; + + let archive_path = temp_dir.path().join("test_archive.tar.gz"); + let file = fs::File::create(&archive_path)?; + let gz_encoder = flate2::write::GzEncoder::new(file, flate2::Compression::default()); + let mut tar_builder = tar::Builder::new(gz_encoder); + tar_builder.append_path_with_name(&test_file_path, "extracted_test_file.txt")?; + tar_builder.into_inner()?.finish()?; + + + Package::extract_archive(archive_path.to_str().unwrap())?; + + + let extracted_file_path = temp_dir.path().join("extracted_test_file.txt"); + assert!(extracted_file_path.exists()); + let content = fs::read_to_string(extracted_file_path)?; + assert_eq!(content, test_file_content); + + Ok(()) + } + + + #[tokio::test] + async fn test_package_check_valid() -> Result<(), Box> { + let temp_dir = TempDir::new()?; + let _config = create_test_config(temp_dir.path().to_str().unwrap()); + + + let install_content = r#" +[package] +name = "test-pkg" +version = "1.0.0" +arch = "X86_64" # Используйте архитектуру хоста или измените для теста архитектуры +descr = "A test package" +license = "MIT" +url = "/repo/test-pkg.mesk" + +[install] +path = "/tmp/test_binary" +user = "root" +group = "root" +mode = "755" +"#; + let install_path = temp_dir.path().join("INSTALL"); + let mut install_file = fs::File::create(&install_path)?; + install_file.write_all(install_content.as_bytes())?; + + + let archive_path = temp_dir.path().join("test_pkg_with_install.tar.gz"); + let file = fs::File::create(&archive_path)?; + let gz_encoder = flate2::write::GzEncoder::new(file, flate2::Compression::default()); + let mut tar_builder = tar::Builder::new(gz_encoder); + tar_builder.append_path_with_name(&install_path, "INSTALL")?; + tar_builder.into_inner()?.finish()?; + + let result = Package::check(archive_path.to_str().unwrap().to_string()); + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), true); + + Ok(()) + } + + + #[tokio::test] + async fn test_package_check_missing_install() -> Result<(), Box> { + let temp_dir = TempDir::new()?; + + let archive_path = temp_dir.path().join("test_pkg_without_install.tar.gz"); + let file = fs::File::create(&archive_path)?; + let gz_encoder = flate2::write::GzEncoder::new(file, flate2::Compression::default()); + let mut tar_builder = tar::Builder::new(gz_encoder); + let dummy_path = temp_dir.path().join("dummy.txt"); + fs::write(&dummy_path, "dummy")?; + tar_builder.append_path_with_name(&dummy_path, "dummy.txt")?; + tar_builder.into_inner()?.finish()?; + + let result = Package::check(archive_path.to_str().unwrap().to_string()); + + assert!(result.is_err()); + let err = result.unwrap_err(); + assert_eq!(err.kind(), std::io::ErrorKind::NotFound); + assert!(err.to_string().contains("INSTALL file not found")); + + Ok(()) + } + + + #[tokio::test] + async fn test_package_check_empty_install() -> Result<(), Box> { + let temp_dir = TempDir::new()?; + + let install_path = temp_dir.path().join("INSTALL"); + fs::write(&install_path, "")?; + let archive_path = temp_dir.path().join("test_pkg_with_empty_install.tar.gz"); + let file = fs::File::create(&archive_path)?; + let gz_encoder = flate2::write::GzEncoder::new(file, flate2::Compression::default()); + let mut tar_builder = tar::Builder::new(gz_encoder); + tar_builder.append_path_with_name(&install_path, "INSTALL")?; + tar_builder.into_inner()?.finish()?; + + + let result = Package::check(archive_path.to_str().unwrap().to_string()); + + assert!(result.is_err()); + let err = result.unwrap_err(); + assert_eq!(err.kind(), std::io::ErrorKind::InvalidData); + assert!(err.to_string().contains("INSTALL file is empty")); + + Ok(()) + } + + + #[tokio::test] + async fn test_package_check_arch_mismatch() -> Result<(), Box> { + let temp_dir = TempDir::new()?; + let _config = create_test_config(temp_dir.path().to_str().unwrap()); + + let host_arch = std::env::consts::ARCH; + let wrong_arch = if host_arch == "x86" { "x86_64" } else { "x86" }; + let install_content = format!(r#" +[package] +name = "test-pkg" +version = "1.0.0" +arch = "{}" +descr = "A test package for arch mismatch" +license = "MIT" +url = "/repo/test-pkg.mesk" + +[install] +path = "/tmp/test_binary" +user = "root" +group = "root" +mode = "755" +"#, wrong_arch); + + let install_path = temp_dir.path().join("INSTALL"); + let mut install_file = fs::File::create(&install_path)?; + install_file.write_all(install_content.as_bytes())?; + + let archive_path = temp_dir.path().join("test_pkg_with_wrong_arch.tar.gz"); + let file = fs::File::create(&archive_path)?; + let gz_encoder = flate2::write::GzEncoder::new(file, flate2::Compression::default()); + let mut tar_builder = tar::Builder::new(gz_encoder); + tar_builder.append_path_with_name(&install_path, "INSTALL")?; + tar_builder.into_inner()?.finish()?; + + let result = Package::check(archive_path.to_str().unwrap().to_string()); + + + assert!(result.is_err()); + let err = result.unwrap_err(); + assert_eq!(err.kind(), std::io::ErrorKind::InvalidData); + assert!(err.to_string().contains("Arch mismatch")); + + Ok(()) + } + +} \ No newline at end of file diff --git a/tests/secondary_funcs.rs b/tests/secondary_funcs.rs new file mode 100644 index 0000000..84034d8 --- /dev/null +++ b/tests/secondary_funcs.rs @@ -0,0 +1,29 @@ +// use mesk::cfg::config::Config; +// use toml; + +/* +fn create_test_config(temp_dir_path: &str) -> Config { + /* + #[derive(Deserialize, Debug, Clone)] + pub struct Config { + pub repo: RepoConfig, + pub paths: PathConfig, + ... + } + + #[derive(Deserialize, Debug, Clone)] + pub struct RepoConfig { + pub repo_url: String, + } + + #[derive(Deserialize, Debug, Clone)] + pub struct PathConfig { + pub cache_dir: String, + ... + } + */ + let cfg = Config::default().unwrap(); + assert!(toml::to_string(&cfg).is_ok()); + cfg +} +*/ \ No newline at end of file diff --git a/tests/shared.rs b/tests/shared.rs new file mode 100644 index 0000000..2ea0bf5 --- /dev/null +++ b/tests/shared.rs @@ -0,0 +1,22 @@ +use mesk::cfg::config::{Config, Repo, Log, Loglevel, Paths}; + +pub fn create_test_config(temp_dir_path: &str) -> Config { + let cfg: Config = Config { + repo: Repo { + repo_url: format!(r"http://mesk.anthrill.i2p/repo/{}", std::env::consts::ARCH), + auto_update: true, + destination: (String::from("mesk"), String::from("mesk")), + repo_http_url: None, + }, + log: Log { + log_file: String::from("/var/log/mesk.log"), + log_level: Loglevel::Info, + }, + paths: Paths { + cache_dir: String::from(temp_dir_path), + build_dir: String::from(format!("{}/build", String::from(temp_dir_path))) + }, + }; + + cfg +} \ No newline at end of file -- cgit v1.2.3