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
59 changes: 43 additions & 16 deletions api/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,47 @@ pub struct CreateBundleUploadResponse {
pub key: String,
pub test_collection_bundle_meta_id: Option<String>,
pub test_collection_bundle_meta_created_at: Option<String>,
/// Repo UUID used to key test collection URLs; absent on older servers.
pub repo_id: Option<String>,
/// Same server-side calculation as `quarantine_resolution_mode`; picks the test URL format.
#[serde(default)]
pub test_collection_migration_state: TestCollectionMigrationState,
}

#[derive(Debug, Serialize, Clone, Copy, PartialEq, Eq, Default)]
#[serde(rename_all = "snake_case")]
pub enum TestCollectionMigrationState {
Repo,
TestCollection,
#[default]
Unspecified,
}

impl<'de> Deserialize<'de> for TestCollectionMigrationState {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Ok(
match serde_json::Value::deserialize(deserializer)?.as_str() {
Some("repo") => Self::Repo,
Some("test_collection") => Self::TestCollection,
_ => Self::Unspecified,
},
)
}
}

impl From<TestCollectionMigrationState>
for proto::upload_metrics::trunk::TestCollectionMigrationState
{
fn from(state: TestCollectionMigrationState) -> Self {
match state {
TestCollectionMigrationState::Repo => Self::Repo,
TestCollectionMigrationState::TestCollection => Self::TestCollection,
TestCollectionMigrationState::Unspecified => Self::Unspecified,
}
}
}

#[derive(Debug, Serialize, Clone, Deserialize, Default)]
Expand All @@ -33,6 +74,8 @@ pub struct GetQuarantineConfigResponse {
pub quarantined_tests: Vec<String>,
#[serde(default)]
pub quarantine_resolution_mode: QuarantineResolutionMode,
/// Repo UUID used to key test collection URLs; absent on older servers.
pub repo_id: Option<String>,
}

#[derive(Debug, Serialize, Clone, Deserialize, PartialEq, Eq)]
Expand All @@ -46,22 +89,6 @@ pub struct GetQuarantineConfigRequest {
pub test_collection_short_id: Option<String>,
}

#[derive(Debug, Serialize, Clone, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct CreateBundleUploadIntentRequest {
pub repo: RepoUrlParts,
pub org_url_slug: String,
pub client_version: String,
}

#[derive(Debug, Serialize, Clone, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct CreateBundleUploadIntentResponse {
pub repo: RepoUrlParts,
pub org_url_slug: String,
pub client_version: String,
}

#[derive(Debug, Serialize, Clone, Deserialize, PartialEq)]
pub struct TelemetryUploadMetricsRequest {
pub upload_metrics: proto::upload_metrics::trunk::UploadMetrics,
Expand Down
121 changes: 89 additions & 32 deletions api/src/urls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,30 @@ use bundle::Test;
use context::repo::RepoUrlParts;
use url::{ParseError, Url, form_urlencoded};

/// Locates a test collection test page, which is keyed by `{repo_id}_{test_case_id}`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CollectionUrlParts {
pub short_id: String,
pub repo_id: String,
}

pub fn url_for_test_case(
public_api_address: &str,
org_url_slug: &String,
repo: &RepoUrlParts,
test_case: &Test,
collection: Option<&CollectionUrlParts>,
) -> Result<String, ParseError> {
let mut url = Url::parse(convert_to_app_url(public_api_address).as_str())?;
url.set_path(test_path(org_url_slug, test_case).as_str());
url.set_query(Some(repo_query(repo).as_str()));
match collection {
Some(collection) => {
url.set_path(collection_test_path(org_url_slug, collection, test_case).as_str());
}
None => {
url.set_path(test_path(org_url_slug, test_case).as_str());
url.set_query(Some(repo_query(repo).as_str()));
}
}
Ok(url.to_string())
}

Expand All @@ -22,44 +37,86 @@ fn test_path(org_url_slug: &String, test_case: &Test) -> String {
format!("{}/flaky-tests/test/{}", org_url_slug, test_case.id)
}

fn collection_test_path(
org_url_slug: &String,
collection: &CollectionUrlParts,
test_case: &Test,
) -> String {
format!(
"{}/flaky-tests/collections/{}/tests/{}_{}",
org_url_slug, collection.short_id, collection.repo_id, test_case.id
)
}

fn repo_query(repo: &RepoUrlParts) -> String {
let value: String =
form_urlencoded::byte_serialize(format!("{}/{}", repo.owner, repo.name).as_bytes())
.collect();
format!("repo={}", value)
}

#[test]
fn test_url_generated() {
let repo = RepoUrlParts {
host: String::from("https://github.com"),
owner: String::from("bad-app"),
name: String::from("ios-app"),
};
#[cfg(test)]
mod tests {
use super::*;

fn test_repo() -> RepoUrlParts {
RepoUrlParts {
host: String::from("https://github.com"),
owner: String::from("bad-app"),
name: String::from("ios-app"),
}
}

fn test_case() -> Test {
Test {
name: String::from("can math"),
parent_name: String::from("basic suite"),
class_name: None,
file: None,
id: String::from("c33a7f64-8f3e-5db9-b37b-2ea870d2441b"),
timestamp_millis: None,
is_quarantined: false,
failure_message: None,
variant: None,
}
}

#[test]
fn test_url_generated() {
let actual = url_for_test_case(
&String::from("https://api.trunk-staging.io"),
&String::from("bad-app-org"),
&test_repo(),
&test_case(),
None,
);

let test = Test {
name: String::from("can math"),
parent_name: String::from("basic suite"),
class_name: None,
file: None,
id: String::from("c33a7f64-8f3e-5db9-b37b-2ea870d2441b"),
timestamp_millis: None,
is_quarantined: false,
failure_message: None,
variant: None,
};
assert_eq!(
actual,
Ok(String::from(
"https://app.trunk-staging.io/bad-app-org/flaky-tests/test/c33a7f64-8f3e-5db9-b37b-2ea870d2441b?repo=bad-app%2Fios-app"
)),
);
}

let actual = url_for_test_case(
&String::from("https://api.trunk-staging.io"),
&String::from("bad-app-org"),
&repo,
&test,
);
#[test]
fn test_collection_url_generated() {
let actual = url_for_test_case(
&String::from("https://api.trunk-staging.io"),
&String::from("bad-app-org"),
&test_repo(),
&test_case(),
Some(&CollectionUrlParts {
short_id: String::from("tc_123"),
repo_id: String::from("12345678-abcd-1234-8678-123456789022"),
}),
);

assert_eq!(
actual,
Ok(String::from(
"https://app.trunk-staging.io/bad-app-org/flaky-tests/test/c33a7f64-8f3e-5db9-b37b-2ea870d2441b?repo=bad-app%2Fios-app"
)),
);
assert_eq!(
actual,
Ok(String::from(
"https://app.trunk-staging.io/bad-app-org/flaky-tests/collections/tc_123/tests/12345678-abcd-1234-8678-123456789022_c33a7f64-8f3e-5db9-b37b-2ea870d2441b"
)),
);
}
}
1 change: 1 addition & 0 deletions cli/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,7 @@ pub async fn gather_exit_code_and_quarantined_tests_context(
org_url_slug: String::default(),
fetch_status: QuarantineFetchStatus::FetchSkipped,
quarantine_resolution_mode: api::message::QuarantineResolutionMode::Unspecified,
collection_url_parts: None,
}
} else {
// default to success if no test run result (i.e. `upload`)
Expand Down
Loading
Loading