Skip to content

Commit

Permalink
test: Test paginated action requests
Browse files Browse the repository at this point in the history
  • Loading branch information
tlater-famedly committed Jan 16, 2025
1 parent ef0d083 commit 8971153
Show file tree
Hide file tree
Showing 2 changed files with 241 additions and 17 deletions.
107 changes: 106 additions & 1 deletion src/v2/management/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,24 @@ impl Zitadel {
self.send_request(request).await
}

/// Search for applications [Docs]()
/// Remove application. [Docs](https://zitadel.com/docs/apis/resources/mgmt/management-service-remove-app)
pub async fn remove_application(
&self,
project_id: String,
application_id: String,
) -> Result<V1RemoveAppResponse> {
let request =
self.client
.delete(self.make_url(&format!(
"management/v1/projects/{project_id}/apps/{application_id}"
))?)
.build()
.context("Error building remove_application request")?;

self.send_request(request).await
}

/// Search for applications [Docs](https://zitadel.com/docs/apis/resources/mgmt/management-service-list-apps)
pub fn list_applications(
&self,
project_id: String,
Expand All @@ -138,6 +155,94 @@ impl Zitadel {

self.send_request(request).await
}

/// Remove project. [Docs](https://zitadel.com/docs/apis/resources/mgmt/management-service-remove-project)
pub async fn remove_project(&self, project_id: String) -> Result<V1RemoveProjectResponse> {
let request = self
.client
.delete(self.make_url(&format!("management/v1/projects/{project_id}"))?)
.build()
.context("Error building delete_project request")?;

self.send_request(request).await
}

/// Search for projects [Docs](https://zitadel.com/docs/apis/resources/mgmt/management-service-list-projects)
pub fn list_projects(
&self,
body: ListProjectsRequest,
) -> Result<impl Stream<Item = V1Project>> {
Ok(PaginationHandler::<_, V1Project>::new(
self.clone(),
body,
self.make_url("management/v1/projects/_search")?,
))
}
}

/// Pagination-supporting project search
#[derive(Clone, Debug)]
pub struct ListProjectsRequest {
inner_request: V1ListProjectsRequest,
}

impl ListProjectsRequest {
/// Constructor
#[must_use]
pub fn new(queries: Vec<V1ProjectQuery>) -> Self {
Self { inner_request: V1ListProjectsRequest::new().with_queries(queries) }
}

/// Use the supplied ListQuery
#[must_use]
pub fn with_query(mut self, query: V1ListQuery) -> Self {
self.inner_request.set_query(query);
self
}

/// Use the supplied application queries
#[must_use]
pub fn with_queries(mut self, queries: Vec<V1ProjectQuery>) -> Self {
self.inner_request.set_queries(queries);
self
}

delegate! {
to self.inner_request {
/// Set the supplied ListQuery
pub fn set_query(&mut self, query: V1ListQuery);
/// The ListQuery currently used for this request
#[must_use] pub fn query(&self) -> Option<&V1ListQuery>;
/// Reset the ListQuery to None
pub fn reset_query(&mut self);
/// Set the supplied app queries
pub fn set_queries(&mut self, queries: Vec<V1ProjectQuery>);
/// The app queries currently used for this request
#[must_use] pub fn queries(&self) -> Option<&Vec<V1ProjectQuery>>;
/// Reset the app queries to None
pub fn reset_queries(&mut self);
}
}
}

impl PaginationRequest for ListProjectsRequest {
type Item = V1ListProjectsRequest;

fn to_paginated_request(&self, page: usize) -> Self::Item {
self.inner_request.clone().with_query(
self.inner_request
.query()
.unwrap_or(&V1ListQuery::new())
.clone()
.with_offset((page * self.page_size()).to_string()),
)
}

fn page_size(&self) -> usize {
(*self.inner_request.query().and_then(|query| query.limit()).unwrap_or(&1000))
.try_into()
.unwrap_or(1000)
}
}

/// Pagination-supporting application search
Expand Down
151 changes: 135 additions & 16 deletions tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,44 @@ async fn tear_down(zitadel: &Zitadel) {
let id = user.user_id().expect("Couldn't get user id during tear down.");
zitadel.delete_user(id).await.expect("Error deleting user");
}

let mut stream = zitadel
.search_actions(ListActionsRequest::new(vec![]))
.expect("Error getting the list of actions to tear down");

while let Some(action) = stream.next().await {
let id = action.id().expect("Couldn't get action id during tear down.");
zitadel.delete_action(id.clone()).await.expect("Error deleting action");
}

let mut stream = zitadel
.list_projects(ListProjectsRequest::new(vec![]))
.expect("Error getting the list of projects to tear down");

while let Some(project) = stream.next().await {
tracing::debug!("{:?}", project);

let id = project.id().expect("Couldn't get project id during tear down.");

let mut stream = zitadel
.list_applications(id.clone(), ListApplicationsRequest::new(vec![]))
.expect("Error getting the list of applications to tear down");

while let Some(application) = stream.next().await {
zitadel
.remove_application(
id.clone(),
application
.id()
.expect("Couldn't get application id during tear down.")
.clone(),
)
.await
.expect("Failed to remove application during teardown");
}

zitadel.remove_project(id.clone()).await.expect("Error deleting project");
}
}

#[tokio::test]
Expand Down Expand Up @@ -109,16 +147,53 @@ async fn test_e2e_create_application() -> Result<()> {

let project_id = project.id().expect("Must have created a project with a valid ID");

let res = zitadel
zitadel
.create_application(
project_id.to_owned(),
ManagementServiceAddApiAppBody::new("TestApp".to_owned()),
)
.await;
.await?;

// TODO: Once we have a list application method, we should check
// that the org was actually created
assert!(res.is_ok());
let stream =
zitadel.list_applications(project_id.to_owned(), ListApplicationsRequest::new(vec![]))?;

let apps: Vec<String> =
stream.map(|app| app.name().expect("App name must be set").clone()).collect().await;
assert_eq!(apps, ["TestApp"]);

tear_down(&zitadel).await;

Ok(())
}

#[test(tokio::test)]
#[test_log(default_log_filter = "debug")]
async fn test_e2e_list_applications() -> Result<()> {
let service_account_file = Path::new(USER_SERVICE_PATH);
let url = Url::parse("http://localhost:8080")?;
let zitadel = Zitadel::new(url, service_account_file.to_path_buf()).await?;

// TODO: Figure out why the teardown fails to delete this project
let project =
zitadel.create_project(V1AddProjectRequest::new("TestProject2".to_owned())).await?;

let project_id = project.id().expect("Must have created a project with a valid ID");

let mut application_names = Vec::new();

for i in 0..10 {
let name = format!("TestApp{i}");
let application = ManagementServiceAddApiAppBody::new(name.clone());
application_names.push(name);
assert!(zitadel.create_application(project_id.to_owned(), application).await.is_ok());
}

let stream =
zitadel.list_applications(project_id.to_owned(), ListApplicationsRequest::new(vec![]))?;

let found_application_names: Vec<String> =
stream.map(|app| app.name().expect("No name found for action").clone()).collect().await;
assert_eq!(found_application_names, application_names);

tear_down(&zitadel).await;

Expand All @@ -139,10 +214,49 @@ async fn test_e2e_create_action() -> Result<()> {
))
.await;

// TODO: Once we have a list action method, we should check that
// the action was actually created
assert!(res.is_ok());

let stream = zitadel.search_actions(ListActionsRequest::new(vec![V1ActionQuery::new()
.with_action_name_query(V1ActionNameQuery::new().with_name("test-action".to_owned()))]))?;

let actions: Vec<String> = stream
.map(|action| action.name().expect("No name found for action").clone())
.collect()
.await;
assert_eq!(actions, vec!["test-action"]);

tear_down(&zitadel).await;

Ok(())
}

#[test(tokio::test)]
#[test_log(default_log_filter = "debug")]
async fn test_e2e_list_actions() -> Result<()> {
let service_account_file = Path::new(USER_SERVICE_PATH);
let url = Url::parse("http://localhost:8080")?;
let zitadel = Zitadel::new(url, service_account_file.to_path_buf()).await?;

let mut action_names = Vec::new();

for i in 0..10 {
let name = format!("test-action-{i}");

let action =
V1CreateActionRequest::new(name.clone(), "console.log('test-action')".to_owned());

action_names.push(name);
assert!(zitadel.create_action(action).await.is_ok());
}

let stream = zitadel.search_actions(ListActionsRequest::new(vec![]))?;

let found_action_names: Vec<String> = stream
.map(|action| action.name().expect("No name found for action").clone())
.collect()
.await;
assert_eq!(found_action_names, action_names);

tear_down(&zitadel).await;

Ok(())
Expand All @@ -164,19 +278,23 @@ async fn test_e2e_update_action() -> Result<()> {

let action_id = res.id().expect("Must have created an action with a valid ID");

let res = zitadel
zitadel
.update_action(
action_id.to_owned(),
ManagementServiceUpdateActionBody::new(
"test-action".to_owned(),
"console.log('test-action-update')".to_owned(),
),
)
.await;
.await?;

// TODO: Once we have a list action method, we should check that
// the action was actually updated
assert!(res.is_ok());
let mut stream = zitadel.search_actions(ListActionsRequest::new(vec![V1ActionQuery::new()
.with_action_name_query(V1ActionNameQuery::new().with_name("test-action".to_owned()))]))?;

let action = stream.next().await.expect("Action must exist");

assert_eq!(action.name(), Some(&"test-action".to_owned()));
assert_eq!(action.script(), Some(&"console.log('test-action-update')".to_owned()));

tear_down(&zitadel).await;

Expand All @@ -199,11 +317,12 @@ async fn test_e2e_delete_action() -> Result<()> {

let action_id = res.id().expect("Must have created an action with a valid ID");

let res = zitadel.delete_action(action_id.to_owned()).await;
zitadel.delete_action(action_id.to_owned()).await?;

// TODO: Once we have a list action method, we should check that
// the action was actually deleted
assert!(res.is_ok());
let stream = zitadel.search_actions(ListActionsRequest::new(vec![V1ActionQuery::new()
.with_action_name_query(V1ActionNameQuery::new().with_name("test-action".to_owned()))]))?;

assert_eq!(stream.count().await, 0);

tear_down(&zitadel).await;

Expand Down

0 comments on commit 8971153

Please sign in to comment.