Skip to content
Merged
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
23 changes: 22 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ members = [
"tests/integration",
"tests/integration-auth",
"tests/o11y",
"tests/openapi",
"tests/protojson-conformance",
"tests/pubsub",
"tests/showcase",
Expand Down Expand Up @@ -474,10 +475,12 @@ language = { default-features = false, version = "1", pat
google-cloud-dns-v1 = { default-features = false, path = "src/generated/cloud/dns/v1" }
google-cloud-showcase-v1beta1 = { default-features = false, path = "src/generated/showcase" }
google-cloud-trace-v1 = { default-features = false, path = "src/generated/devtools/cloudtrace/v1" }
secretmanager-openapi-v1 = { default-features = false, path = "src/generated/openapi-validation" }
# The names on these are too long, I do not want to reformat the whole file for them.
google-cloud-workflows-v1 = { default-features = false, path = "src/generated/cloud/workflows/v1" }
google-cloud-workflows-executions-v1 = { default-features = false, path = "src/generated/cloud/workflows/executions/v1" }


# Local test packages (never add a version)
google-cloud-test-utils = { default-features = false, path = "src/test-utils" }
integration-tests = { path = "tests/integration" }
Expand Down
44 changes: 44 additions & 0 deletions src/test-utils/src/resource_names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ use rand::{
/// Where possible, we use this prefix for randomly generated resource ids.
pub const PREFIX: &str = "rust-sdk-testing-";

/// The maximum length for a secret ID.
const SECRET_ID_LENGTH: usize = 64;

const BUCKET_ID_LENGTH: usize = 63;

const WORKFLOW_ID_LENGTH: usize = 64;
Expand All @@ -44,6 +47,16 @@ pub fn random_workflow_id() -> String {
format!("{PREFIX}{id}")
}

/// Generate a random secret id.
pub fn random_secret_id() -> String {
let id: String = rand::rng()
.sample_iter(&Alphanumeric)
.take(SECRET_ID_LENGTH - PREFIX.len())
.map(char::from)
.collect();
format!("{PREFIX}{id}")
}

const LOWERCASE_ALPHANUMERIC_CHARSET: &[u8] = b"abcdefghijklmnopqrstuvwxyz0123456789";

/// Sample a `u8`, uniformly distributed over ASCII lowercase letters and numbers: a-z and 0-9.
Expand Down Expand Up @@ -84,6 +97,11 @@ mod tests {

#[test]
fn bucket_id() {
assert!(
PREFIX.len() < BUCKET_ID_LENGTH,
"{PREFIX} length ({}) should be smaller than {BUCKET_ID_LENGTH}",
PREFIX.len()
);
let got = random_bucket_id();
assert!(
got.len() <= BUCKET_ID_LENGTH,
Expand All @@ -97,6 +115,11 @@ mod tests {

#[test]
fn workflow_id() {
assert!(
PREFIX.len() < WORKFLOW_ID_LENGTH,
"{PREFIX} length ({}) should be smaller than {WORKFLOW_ID_LENGTH}",
PREFIX.len()
);
let got = random_workflow_id();
assert!(
got.len() <= WORKFLOW_ID_LENGTH,
Expand All @@ -111,6 +134,27 @@ mod tests {
);
}

#[test]
fn secret_id() {
assert!(
PREFIX.len() < SECRET_ID_LENGTH,
"{PREFIX} length ({}) should be smaller than {SECRET_ID_LENGTH}",
PREFIX.len()
);
let got = random_secret_id();
assert!(
got.len() <= SECRET_ID_LENGTH,
"{got} has more than {SECRET_ID_LENGTH} characters"
);
let suffix = got
.strip_prefix(PREFIX)
.expect("{got} should start with {PREFIX}");
assert!(
suffix.chars().all(|c| c.is_alphanumeric()),
"the suffix should be alphanumeric: {suffix}"
);
}

#[test]
fn lowercase() {
let got: String = rand::rng()
Expand Down
4 changes: 0 additions & 4 deletions tests/integration/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,6 @@ path = "../../src/firestore"
package = "google-cloud-secretmanager-v1"
path = "../../src/generated/cloud/secretmanager/v1"

[dependencies.smo]
package = "secretmanager-openapi-v1"
path = "../../src/generated/openapi-validation"

[dependencies.storage]
package = "google-cloud-storage"
path = "../../src/storage"
Expand Down
2 changes: 0 additions & 2 deletions tests/integration/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ pub mod secret_manager;
pub mod storage;
pub mod workflows;

pub const SECRET_ID_LENGTH: usize = 64;

pub const VM_ID_LENGTH: usize = 63;

pub fn report_error(e: anyhow::Error) -> anyhow::Error {
Expand Down
2 changes: 0 additions & 2 deletions tests/integration/src/secret_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod openapi;
pub mod openapi_locational;
pub mod protobuf;
8 changes: 2 additions & 6 deletions tests/integration/src/secret_manager/protobuf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,12 @@

use crate::Result;
use gax::paginator::{ItemPaginator, Paginator};
use google_cloud_test_utils::resource_names::random_secret_id;
use google_cloud_test_utils::runtime_config::{project_id, test_service_account};
use rand::{Rng, distr::Alphanumeric};

pub async fn run(builder: sm::builder::secret_manager_service::ClientBuilder) -> Result<()> {
let project_id = project_id()?;
let secret_id: String = rand::rng()
.sample_iter(&Alphanumeric)
.take(crate::SECRET_ID_LENGTH)
.map(char::from)
.collect();
let secret_id = random_secret_id();

let client = builder.build().await?;
cleanup_stale_secrets(&client, &project_id, &secret_id).await?;
Expand Down
16 changes: 0 additions & 16 deletions tests/integration/tests/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,22 +129,6 @@ mod driver {
.map_err(integration_tests::report_error)
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn run_secretmanager_openapi() -> integration_tests::Result<()> {
let _guard = enable_tracing();
integration_tests::secret_manager::openapi::run()
.await
.map_err(integration_tests::report_error)
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn run_secretmanager_openapi_locational() -> integration_tests::Result<()> {
let _guard = enable_tracing();
integration_tests::secret_manager::openapi_locational::run()
.await
.map_err(integration_tests::report_error)
}

#[test_case(StorageControl::builder().with_tracing().with_retry_policy(retry_policy()); "with tracing and retry enabled")]
#[test_case(StorageControl::builder().with_endpoint("https://www.googleapis.com"); "with global endpoint")]
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
Expand Down
17 changes: 0 additions & 17 deletions tests/integration/tests/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,6 @@
#[cfg(test)]
mod serialization {
use anyhow::Result;
use static_assertions::{assert_impl_all, assert_not_impl_any};

// The generator introduces synthetic messages for the requests in
// OpenAPI-based services. Those should not have serialization or
// deserialization functions.
#[test]
fn synthetic_message_serialization() -> Result<()> {
use smo::model::{Secret, secret_manager_service::CreateSecretRequest};

assert_impl_all!(CreateSecretRequest: std::fmt::Debug);
assert_not_impl_any!(CreateSecretRequest: serde::Serialize);
assert_not_impl_any!(CreateSecretRequest: serde::de::DeserializeOwned);
assert_impl_all!(Secret: std::fmt::Debug);
assert_impl_all!(Secret: serde::Serialize);
assert_impl_all!(Secret: serde::de::DeserializeOwned);
Ok(())
}

#[test]
fn multiple_serde_attributes() -> Result<()> {
Expand Down
49 changes: 49 additions & 0 deletions tests/openapi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

[package]
description = "Integration tests for an OpenAPI-based service."
edition.workspace = true
name = "integration-tests-openapi"
publish = false
version = "0.0.0"

[features]
# These are normally disabled because they run against production.
run-integration-tests = []
# Enable additional logging in the integration tests.
log-integration-tests = ["google-cloud-test-utils/log-integration-tests"]

[dependencies]
anyhow.workspace = true
bytes.workspace = true
chrono = { workspace = true, features = ["now"] }
crc32c.workspace = true
futures.workspace = true
google-cloud-gax.workspace = true
google-cloud-lro.workspace = true
google-cloud-test-utils = { workspace = true }
google-cloud-wkt.workspace = true
secretmanager-openapi-v1 = { workspace = true, features = ["default"] }
serde.workspace = true
tokio.workspace = true
tracing.workspace = true
tracing-subscriber = { workspace = true, features = ["fmt"] }

[dev-dependencies]
static_assertions.workspace = true
test-case.workspace = true

[lints]
workspace = true
17 changes: 17 additions & 0 deletions tests/openapi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Integration tests for an OpenAPI-based client

[Sidekick] supports multiple different specification formats, including OpenAPI.
This directory contains integration tests for one client generated from an
OpenAPI specification.

We use the [Secret Manager] service and client in these tests as:

- Secret Manager does not have very restrictive quota requirements.
- Secret Manager uses many different field types, including maps, bytes an anys.
- Secret Manager has pagination methods, some mixins, and regional endpoints.

The one large gap is LROs, but it is unclear how LROs will work for OpenAPI in
the first place.

[secret manager]: https://docs.cloud.google.com/secret-manager/docs
[sidekick]: https://github.com/googleapis/librarian
Loading
Loading