Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions operator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,10 @@ serde_json = "1.0.141"
chrono = "0.4.41"
json-patch = "4.0.0"
jsonptr = "0.7.1"

[dev-dependencies]
serde_json = "1.0"
hyper = { version = "1", features = ["full"] }
tower = { version = "0.4", features = ["full"] }
tower-test = "0.4.0"
http-body-util = "0.1"
74 changes: 74 additions & 0 deletions operator/src/trustee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,3 +365,77 @@ pub async fn generate_kbs(

Ok(())
}

#[cfg(test)]
mod tests {
use super::*;
use http_body_util::BodyExt;
use hyper::http;
use kube::client::Body;
use serde_json::json;
use tower::ServiceBuilder;
use tower_test::mock;

#[tokio::test]
async fn test_generate_kbs_auth_public_key_creates_secret() {
// 1. Setup the mock K8s API server
let (mock_service, mut handle) = mock::pair::<http::Request<Body>, http::Response<Body>>();
let service = ServiceBuilder::new().service(mock_service);
let client = Client::new(service, "default");

// 2. Define the expected API call and the response in a separate task
let handle = tokio::spawn(async move {
// Expect a single request to create a Secret
let (request, send) = handle
.next_request()
.await
.expect("service received a request");

// Assertions on the request
assert_eq!(request.method(), "POST");
assert_eq!(
request.uri().path(),
"/api/v1/namespaces/test-namespace/secrets"
);

// Extract and deserialize the body to check its content
let body_bytes = request.into_body().collect().await.unwrap().to_bytes();
let secret: Secret = serde_json::from_slice(&body_bytes).unwrap();
assert_eq!(secret.metadata.name.as_deref(), Some("test-secret-name"));

// Check that the publicKey field exists and is not empty
let data = secret.data.unwrap();
let public_key_b64 = data.get("publicKey").unwrap();
assert!(!public_key_b64.0.is_empty());
Comment on lines +403 to +409
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, extract this part and include it in the setup of the test. See the comment: https://github.com/confidential-clusters/cocl-operator/pull/20/files#r2340577927


// Send back a successful response
let response_secret = json!({
"apiVersion": "v1",
"kind": "Secret",
"metadata": {
"name": "test-secret-name",
"namespace": "test-namespace"
Comment on lines +413 to +417
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we also reuse the kubernetes structures defined in the kube-rs crate. We shouldn really not hardcode any of the kubernetes objects

}
});
let response = http::Response::builder()
.status(201)
.body(Body::from(response_secret.to_string().into_bytes()))
.unwrap();
send.send_response(response);
});
Comment on lines +381 to +425
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we create a module where we define the kubernetes mocked client? This logic should be reused multiple times by the tests


// 3. Call the function under test
let result =
generate_kbs_auth_public_key(client, "test-namespace", "test-secret-name").await;

// 4. Assert the function's result
assert!(result.is_ok());

// Wait for the mock server task to finish its assertions
handle.await.unwrap();

// 5. Cleanup filesystem side effects
let _ = fs::remove_file("privateKey");
let _ = fs::remove_file("publicKey");
Comment on lines +436 to +439
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does rust have some teardown logic? I will be much more clean if this was separated from the main logic of the test

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing if the test requires some setup to make it working. This could be for example the initialization of the mocking frameworking

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For rust, apprently there isn't a framework with builtin setup and teardonw, but at least for teardown you could create a drop function, see https://stackoverflow.com/questions/73008377/rust-create-test-setup

}
}