agama/rust/agama-software/tests/zypp_server.rs
2026-05-20 11:57:56 +05:30

160 lines
5.1 KiB
Rust

// Copyright (c) [2025] SUSE LLC
//
// All Rights Reserved.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, contact SUSE LLC.
//
// To contact SUSE LLC about this file by physical or electronic mail, you may
// find current contact information at www.suse.com.
use agama_security as security;
use agama_software::state::{Repository as StateRepository, SoftwareState};
use agama_software::zypp_server::{SoftwareAction, ZyppServer, ZyppServerResult};
use agama_software::WriteIssues;
use agama_utils::{
actor,
api::{
event::Event,
question::{Answer, AnswerRule, Config},
},
progress, question,
};
use camino::{Utf8Path, Utf8PathBuf};
use glob::glob;
use std::fs;
use std::path::Path;
use std::result::Result;
use tokio::sync::{broadcast, oneshot};
fn cleanup_past_leftovers(root_dir: &Path) {
remove_repos(root_dir);
remove_rpmdb(root_dir);
}
fn remove_repos(root_dir: &Path) {
let repo_dir = root_dir.join("etc/zypp/repos.d/");
for path in glob(&format!("{}/*.repo", repo_dir.display()))
// unwrap OK: literal pattern syntax is correct
.unwrap()
.filter_map(Result::ok)
{
fs::remove_file(path).expect("failed to remove repo file");
}
}
fn remove_rpmdb(root_dir: &Path) {
let rpmdb_dir = root_dir.join("usr/lib/sysimage/rpm/");
if fs::exists(&rpmdb_dir).unwrap_or(false) {
fs::remove_dir_all(rpmdb_dir).expect("removing RPM data failed");
}
}
#[tokio::test]
async fn test_start_zypp_server() {
let _ = tracing_subscriber::fmt::try_init();
let root_dir =
Utf8Path::new(env!("CARGO_MANIFEST_DIR")).join("../zypp-agama/fixtures/zypp_repos_root");
cleanup_past_leftovers(root_dir.as_std_path());
let zypp_root =
Utf8Path::new(env!("CARGO_MANIFEST_DIR")).join("../zypp-agama/fixtures/zypp_root_tmp");
let install_dir = Utf8PathBuf::from("/mnt");
let cmdline = Default::default();
let client =
ZyppServer::start(&zypp_root, &install_dir, &cmdline).expect("starting zypp server failed");
// Setup event broadcast channel
let (event_tx, _event_rx) = broadcast::channel::<Event>(100); // Buffer size 100
// Spawn progress service
let progress_service_starter = progress::service::Starter::new(event_tx.clone());
let progress_handler = progress_service_starter.start();
// Spawn question service
let question_service = question::service::Service::new(event_tx.clone());
let question_handler = actor::spawn(question_service);
// Spawn the security service
let security_service_starter = security::service::Starter::new(question_handler.clone());
let security_handler = security_service_starter.start().unwrap();
// Pre-configure the answer to the GPG key question
let answer = Answer {
action: "Trust".to_string(),
value: None,
};
let rule = AnswerRule {
class: Some("software.import_gpg".to_string()),
text: None,
data: None,
answer,
};
let config = Config {
policy: None,
answers: Some(vec![rule]),
};
question_handler
.call(question::message::SetConfig::new(Some(config)))
.await
.unwrap();
let (tx, rx) = oneshot::channel();
let repo_s = StateRepository {
name: "signed_repo".to_string(),
alias: "signed_repo".to_string(),
url: root_dir.join("usr/share/signed_repo").to_string(),
enabled: true,
priority: None,
};
let mut software_state = SoftwareState::new("test_product");
software_state.repositories = vec![repo_s];
client
.send(SoftwareAction::Write {
state: software_state,
progress: progress_handler,
l10n: None,
question: question_handler.clone(),
security: security_handler,
tx,
})
.expect("Failed to send SoftwareAction::Write");
let result: ZyppServerResult<WriteIssues> =
rx.await.expect("Failed to receive response from server");
assert!(
result.is_ok(),
"SoftwareAction::Write failed: {:?}",
result.unwrap_err()
);
let issues = result.unwrap();
assert_eq!(
issues.software.len(),
1,
"There are unexpected issues size {issues:#?}"
);
assert_eq!(issues.software[0].class, "missing_product");
let questions = question_handler
.call(question::message::Get)
.await
.expect("Failed to get questions");
assert!(questions.is_empty());
// NOTE: here we drop sender channel, which result in exit of zypp thread due to closed channel
}