From e1e9a29d5db1330eb51c1e84e5c6c3acb9b4f65e Mon Sep 17 00:00:00 2001
From: John DiSanti <jdisanti@amazon.com>
Date: Wed, 11 May 2022 16:46:04 -0700
Subject: [PATCH] Fix some bugs in the `sdk-sync` tool (#1382)

---
 .../{git-ff-merge => git-delete-branch}       |   2 +-
 tools/sdk-sync/fake-cli/git-squash-merge      |  19 +
 tools/sdk-sync/src/git.rs                     |  59 +-
 tools/sdk-sync/src/sync.rs                    | 118 ++--
 tools/sdk-sync/tests/create-test-workspace    |   6 +
 tools/sdk-sync/tests/e2e_test.rs              |  13 +-
 tools/sdk-sync/tests/mock_e2e_test.rs         | 605 ------------------
 7 files changed, 124 insertions(+), 698 deletions(-)
 rename tools/sdk-sync/fake-cli/{git-ff-merge => git-delete-branch} (68%)
 create mode 100755 tools/sdk-sync/fake-cli/git-squash-merge
 delete mode 100644 tools/sdk-sync/tests/mock_e2e_test.rs

diff --git a/tools/sdk-sync/fake-cli/git-ff-merge b/tools/sdk-sync/fake-cli/git-delete-branch
similarity index 68%
rename from tools/sdk-sync/fake-cli/git-ff-merge
rename to tools/sdk-sync/fake-cli/git-delete-branch
index d0ee7b2df..6adc10036 100755
--- a/tools/sdk-sync/fake-cli/git-ff-merge
+++ b/tools/sdk-sync/fake-cli/git-delete-branch
@@ -2,7 +2,7 @@
 import os
 import sys
 
-expected = [os.path.realpath("/tmp"), ["merge", "--ff-only", "test-branch-name"]]
+expected = [os.path.realpath("/tmp"), ["branch", "-D", "test-branch-name"]]
 actual = [os.getcwd(), sys.argv[1:]]
 if expected != actual:
     print(f"ERROR\nExpect: {expected}\nActual: {actual}")
diff --git a/tools/sdk-sync/fake-cli/git-squash-merge b/tools/sdk-sync/fake-cli/git-squash-merge
new file mode 100755
index 000000000..7be2a54b6
--- /dev/null
+++ b/tools/sdk-sync/fake-cli/git-squash-merge
@@ -0,0 +1,19 @@
+#!/usr/bin/env python3
+import os
+import sys
+
+if sys.argv[1] == "merge":
+    expected = [os.path.realpath("/tmp"), ["merge", "--squash", "test-branch-name"]]
+    actual = [os.getcwd(), sys.argv[1:]]
+    if expected != actual:
+        print(f"ERROR\nExpect: {expected}\nActual: {actual}")
+        sys.exit(1)
+else:
+    expected = [
+        os.path.realpath("/tmp"),
+        ["-c", "user.name=test-author", "-c", "user.email=test-author-email", "commit", "-m", "test message"]
+    ]
+    actual = [os.getcwd(), sys.argv[1:]]
+    if expected != actual:
+        print(f"ERROR\nExpect: {expected}\nActual: {actual}")
+        sys.exit(1)
diff --git a/tools/sdk-sync/src/git.rs b/tools/sdk-sync/src/git.rs
index f9e23488e..d114ea5ca 100644
--- a/tools/sdk-sync/src/git.rs
+++ b/tools/sdk-sync/src/git.rs
@@ -101,8 +101,17 @@ pub trait Git: Send + Sync {
     /// Creates a branch at the given revision.
     fn create_branch(&self, branch_name: &str, revision: &str) -> Result<()>;
 
-    /// Fast-forward merges a branch.
-    fn fast_forward_merge(&self, branch_name: &str) -> Result<()>;
+    /// Deletes a branch.
+    fn delete_branch(&self, branch_name: &str) -> Result<()>;
+
+    /// Squash merges a branch into the current branch.
+    fn squash_merge(
+        &self,
+        author_name: &str,
+        author_email: &str,
+        branch_name: &str,
+        commit_message: &str,
+    ) -> Result<()>;
 
     /// Returns list of untracked files.
     fn untracked_files(&self) -> Result<Vec<PathBuf>>;
@@ -322,18 +331,38 @@ impl Git for GitCLI {
         Ok(())
     }
 
-    fn fast_forward_merge(&self, branch_name: &str) -> Result<()> {
+    fn delete_branch(&self, branch_name: &str) -> Result<()> {
         let mut command = Command::new(&self.binary_name);
-        command.arg("merge");
-        command.arg("--ff-only");
+        command.arg("branch");
+        command.arg("-D");
         command.arg(branch_name);
         command.current_dir(&self.repo_path);
 
         let output = log_command(command).output()?;
-        handle_failure("fast_forward_merge", &output)?;
+        handle_failure("delete_branch", &output)?;
         Ok(())
     }
 
+    fn squash_merge(
+        &self,
+        author_name: &str,
+        author_email: &str,
+        branch_name: &str,
+        commit_message: &str,
+    ) -> Result<()> {
+        let mut command = Command::new(&self.binary_name);
+        command.arg("merge");
+        command.arg("--squash");
+        command.arg(branch_name);
+        command.current_dir(&self.repo_path);
+
+        let output = log_command(command).output()?;
+        handle_failure("squash_merge", &output)?;
+
+        // `git merge --squash` only stages changes, so a commit is necessary after
+        self.commit(author_name, author_email, commit_message)
+    }
+
     fn untracked_files(&self) -> Result<Vec<PathBuf>> {
         let mut command = Command::new(&self.binary_name);
         command.arg("ls-files");
@@ -564,9 +593,21 @@ mod tests {
     }
 
     #[test]
-    fn fast_forward_merge() {
-        cli("git-ff-merge")
-            .fast_forward_merge("test-branch-name")
+    fn delete_branch() {
+        cli("git-delete-branch")
+            .delete_branch("test-branch-name")
+            .expect("successful invocation");
+    }
+
+    #[test]
+    fn squash_merge() {
+        cli("git-squash-merge")
+            .squash_merge(
+                "test-author",
+                "test-author-email",
+                "test-branch-name",
+                "test message",
+            )
             .expect("successful invocation");
     }
 }
diff --git a/tools/sdk-sync/src/sync.rs b/tools/sdk-sync/src/sync.rs
index 22d4d92d8..7e9c819ac 100644
--- a/tools/sdk-sync/src/sync.rs
+++ b/tools/sdk-sync/src/sync.rs
@@ -5,7 +5,7 @@
 
 use self::gen::{DefaultSdkGenerator, SdkGenerator};
 use crate::fs::{DefaultFs, Fs};
-use crate::git::{Commit, CommitHash, Git, GitCLI};
+use crate::git::{Commit, Git, GitCLI};
 use crate::versions::{DefaultVersions, Versions, VersionsManifest};
 use anyhow::{bail, Context, Result};
 use smithy_rs_tool_common::macros::here;
@@ -21,49 +21,12 @@ pub const BOT_NAME: &str = "AWS SDK Rust Bot";
 pub const BOT_EMAIL: &str = "aws-sdk-rust-primary@amazon.com";
 pub const MODEL_STASH_BRANCH_NAME: &str = "__sdk_sync__models_";
 
-#[cfg_attr(test, mockall::automock)]
-pub trait CreateSdkGenerator: Send + std::marker::Sync {
-    fn create_sdk_generator(
-        &self,
-        aws_doc_sdk_examples_revision: &CommitHash,
-        examples_path: &Path,
-        fs: Arc<dyn Fs>,
-        reset_to_commit: Option<CommitHash>,
-        original_smithy_rs_path: &Path,
-    ) -> Result<Box<dyn SdkGenerator>>;
-}
-
-pub struct DefaultCreateSdkGenerator;
-
-impl CreateSdkGenerator for DefaultCreateSdkGenerator {
-    fn create_sdk_generator(
-        &self,
-        aws_doc_sdk_examples_revision: &CommitHash,
-        examples_path: &Path,
-        fs: Arc<dyn Fs>,
-        reset_to_commit: Option<CommitHash>,
-        original_smithy_rs_path: &Path,
-    ) -> Result<Box<dyn SdkGenerator>> {
-        Ok(Box::new(
-            DefaultSdkGenerator::new(
-                aws_doc_sdk_examples_revision,
-                examples_path,
-                fs,
-                reset_to_commit,
-                original_smithy_rs_path,
-            )
-            .context(here!())?,
-        ))
-    }
-}
-
 pub struct Sync {
     aws_doc_sdk_examples: Arc<dyn Git>,
     aws_sdk_rust: Arc<dyn Git>,
     smithy_rs: Arc<dyn Git>,
     fs: Arc<dyn Fs>,
     versions: Arc<dyn Versions>,
-    create_sdk_generator: Arc<dyn CreateSdkGenerator>,
 }
 
 impl Sync {
@@ -78,7 +41,6 @@ impl Sync {
             smithy_rs: Arc::new(GitCLI::new(smithy_rs_path)?),
             fs: Arc::new(DefaultFs::new()) as Arc<dyn Fs>,
             versions: Arc::new(DefaultVersions::new()),
-            create_sdk_generator: Arc::new(DefaultCreateSdkGenerator),
         })
     }
 
@@ -88,7 +50,6 @@ impl Sync {
         smithy_rs: impl Git + 'static,
         fs: impl Fs + 'static,
         versions: impl Versions + 'static,
-        create_sdk_generator: impl CreateSdkGenerator + 'static,
     ) -> Self {
         Self {
             aws_doc_sdk_examples: Arc::new(aws_doc_sdk_examples),
@@ -96,7 +57,6 @@ impl Sync {
             smithy_rs: Arc::new(smithy_rs),
             fs: Arc::new(fs),
             versions: Arc::new(versions),
-            create_sdk_generator: Arc::new(create_sdk_generator),
         }
     }
 
@@ -155,23 +115,31 @@ impl Sync {
     fn sync_model_changes(&self, versions: &VersionsManifest) -> Result<()> {
         info!("Syncing model changes...");
 
-        // Restore the model changes
+        // Restore the model changes. Note: endpoints.json/default config/model changes
+        // may each be in their own commits coming into this, but we want them squashed into
+        // one commit for smithy-rs.
+        self.smithy_rs
+            .squash_merge(
+                BOT_NAME,
+                BOT_EMAIL,
+                MODEL_STASH_BRANCH_NAME,
+                "Update SDK models",
+            )
+            .context(here!())?;
         self.smithy_rs
-            .fast_forward_merge(MODEL_STASH_BRANCH_NAME)
+            .delete_branch(MODEL_STASH_BRANCH_NAME)
             .context(here!())?;
         let model_change_commit = self.smithy_rs.show("HEAD").context(here!())?;
 
         // Generate with the original examples
-        let sdk_gen = self
-            .create_sdk_generator
-            .create_sdk_generator(
-                &versions.aws_doc_sdk_examples_revision,
-                &self.aws_sdk_rust.path().join("examples"),
-                self.fs.clone(),
-                None,
-                self.smithy_rs.path(),
-            )
-            .context(here!("failed to generate the SDK"))?;
+        let sdk_gen = DefaultSdkGenerator::new(
+            &versions.aws_doc_sdk_examples_revision,
+            &self.aws_sdk_rust.path().join("examples"),
+            self.fs.clone(),
+            None,
+            self.smithy_rs.path(),
+        )
+        .context(here!())?;
         let generated_sdk = sdk_gen.generate_sdk().context(here!())?;
         self.copy_sdk(generated_sdk.path())
             .context(here!("failed to copy the SDK"))?;
@@ -214,7 +182,6 @@ impl Sync {
         // Generate code in parallel for each individual commit
         let code_gen_paths = {
             let smithy_rs = self.smithy_rs.clone();
-            let create_sdk_generator = self.create_sdk_generator.clone();
             let examples_revision = versions.aws_doc_sdk_examples_revision.clone();
             let examples_path = self.aws_sdk_rust.path().join("examples");
             let fs = self.fs.clone();
@@ -234,15 +201,14 @@ impl Sync {
                         format!("couldn't find commit {} in smithy-rs", commit_hash)
                     })?;
 
-                    let sdk_gen = create_sdk_generator
-                        .create_sdk_generator(
-                            &examples_revision,
-                            &examples_path,
-                            fs.clone(),
-                            Some(commit.hash.clone()),
-                            smithy_rs.path(),
-                        )
-                        .context(here!())?;
+                    let sdk_gen = DefaultSdkGenerator::new(
+                        &examples_revision,
+                        &examples_path,
+                        fs.clone(),
+                        Some(commit.hash.clone()),
+                        smithy_rs.path(),
+                    )
+                    .context(here!())?;
                     let sdk_path = sdk_gen.generate_sdk().context(here!())?;
                     Ok((commit, sdk_path))
                 })
@@ -285,16 +251,14 @@ impl Sync {
         }
         let examples_head = example_revisions.iter().cloned().next().unwrap();
 
-        let sdk_gen = self
-            .create_sdk_generator
-            .create_sdk_generator(
-                &examples_head,
-                &self.aws_doc_sdk_examples.path().join("rust_dev_preview"),
-                self.fs.clone(),
-                None,
-                self.smithy_rs.path(),
-            )
-            .context(here!())?;
+        let sdk_gen = DefaultSdkGenerator::new(
+            &examples_head,
+            &self.aws_doc_sdk_examples.path().join("rust_dev_preview"),
+            self.fs.clone(),
+            None,
+            self.smithy_rs.path(),
+        )
+        .context(here!())?;
         let generated_sdk = sdk_gen.generate_sdk().context(here!())?;
         self.copy_sdk(generated_sdk.path())
             .context("failed to copy the SDK")?;
@@ -445,7 +409,7 @@ impl Sync {
 mod tests {
     use super::*;
     use crate::fs::MockFs;
-    use crate::git::MockGit;
+    use crate::git::{CommitHash, MockGit};
     use crate::versions::MockVersions;
 
     // Wish this was in std...
@@ -544,7 +508,6 @@ mod tests {
             MockGit::new(),
             MockFs::new(),
             MockVersions::new(),
-            MockCreateSdkGenerator::new(),
         );
         assert!(sync
             .commit_sdk_changes(
@@ -596,7 +559,6 @@ mod tests {
             MockGit::new(),
             MockFs::new(),
             MockVersions::new(),
-            MockCreateSdkGenerator::new(),
         );
         assert!(sync
             .commit_sdk_changes(
@@ -630,7 +592,6 @@ mod tests {
             MockGit::new(),
             MockFs::new(),
             MockVersions::new(),
-            MockCreateSdkGenerator::new(),
         );
 
         assert!(
@@ -657,7 +618,6 @@ mod tests {
             MockGit::new(),
             MockFs::new(),
             MockVersions::new(),
-            MockCreateSdkGenerator::new(),
         );
 
         assert!(
@@ -684,7 +644,6 @@ mod tests {
             MockGit::new(),
             MockFs::new(),
             MockVersions::new(),
-            MockCreateSdkGenerator::new(),
         );
 
         assert!(sync.sdk_has_changes().unwrap(), "it should have changes");
@@ -713,7 +672,6 @@ mod tests {
             MockGit::new(),
             MockFs::new(),
             MockVersions::new(),
-            MockCreateSdkGenerator::new(),
         );
 
         assert!(sync.sdk_has_changes().unwrap(), "it should have changes");
diff --git a/tools/sdk-sync/tests/create-test-workspace b/tools/sdk-sync/tests/create-test-workspace
index 5fe8ab1ee..1e5c54aec 100755
--- a/tools/sdk-sync/tests/create-test-workspace
+++ b/tools/sdk-sync/tests/create-test-workspace
@@ -18,6 +18,8 @@ if [[ $# -eq 1 && "$1" == "--with-model-changes" ]]; then
     INCLUDE_MODEL_CHANGES=1
 fi
 
+ENDPOINTS_JSON_PATH="aws/sdk-codegen/src/main/resources/software/amazon/smithy/rustsdk/endpoints.json"
+
 mkdir aws-doc-sdk-examples
 mkdir aws-sdk-rust
 mkdir smithy-rs
@@ -42,7 +44,9 @@ mkdir -p aws/sdk/aws-models
 mkdir -p aws/sdk/examples
 mkdir -p aws/sdk/build/aws-sdk/examples/s3
 mkdir -p aws/sdk/build/aws-sdk/sdk/s3
+mkdir -p $(dirname "${ENDPOINTS_JSON_PATH}")
 echo "Ancient S3 model" > aws/sdk/aws-models/s3.json
+echo "Old endpoints.json" > "${ENDPOINTS_JSON_PATH}"
 echo "Some S3 client code" > aws/sdk/build/aws-sdk/sdk/s3/fake_content
 cat "${SCRIPT_PATH}/fake-sdk-assemble" > gradlew
 chmod +x gradlew
@@ -65,6 +69,8 @@ if [[ "${INCLUDE_MODEL_CHANGES}" == "1" ]]; then
     pushd smithy-rs
     echo "Updated S3 model" > aws/sdk/aws-models/s3.json
     git -c user.name="Automated Process" -c user.email="bot@example.com" commit -am "Update the S3 model"
+    echo "Updated endpoints.json" > "${ENDPOINTS_JSON_PATH}"
+    git -c user.name="Automated Process" -c user.email="bot@example.com" commit -am "Update endpoints.json"
     popd
 fi
 
diff --git a/tools/sdk-sync/tests/e2e_test.rs b/tools/sdk-sync/tests/e2e_test.rs
index c4fb3bd13..981abf864 100644
--- a/tools/sdk-sync/tests/e2e_test.rs
+++ b/tools/sdk-sync/tests/e2e_test.rs
@@ -14,6 +14,9 @@ use std::path::Path;
 use std::process::Command;
 use tempfile::TempDir;
 
+const ENDPOINTS_JSON_PATH: &str =
+    "aws/sdk-codegen/src/main/resources/software/amazon/smithy/rustsdk/endpoints.json";
+
 static INIT_TRACING: Lazy<bool> = Lazy::new(|| {
     init_tracing();
     true
@@ -238,7 +241,7 @@ fn test_with_model_changes() {
 
     assert_eq!(BOT_NAME, sdk_commits[1].author_name);
     assert_eq!(BOT_EMAIL, sdk_commits[1].author_email);
-    assert_eq!("Update the S3 model", sdk_commits[1].message_subject);
+    assert_eq!("Update SDK models", sdk_commits[1].message_subject);
     assert_eq!("", sdk_commits[1].message_body);
 
     assert_eq!("Another Dev", sdk_commits[2].author_name);
@@ -272,9 +275,13 @@ fn test_with_model_changes() {
         "Some modified S3 example\n",
     );
 
-    // Verify smithy-rs had no changes since we don't have model updates
-    assert_eq!(
+    // Verify smithy-rs has the model updates
+    assert_ne!(
         smithy_rs_start_revision,
         smithy_rs.get_head_revision().unwrap()
     );
+    assert_file_contents(
+        smithy_rs.path().join(ENDPOINTS_JSON_PATH),
+        "Updated endpoints.json\n",
+    );
 }
diff --git a/tools/sdk-sync/tests/mock_e2e_test.rs b/tools/sdk-sync/tests/mock_e2e_test.rs
deleted file mode 100644
index 92bd99be1..000000000
--- a/tools/sdk-sync/tests/mock_e2e_test.rs
+++ /dev/null
@@ -1,605 +0,0 @@
-/*
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0
- */
-
-use anyhow::Result;
-use mockall::{predicate::*, Sequence};
-use once_cell::sync::Lazy;
-use sdk_sync::fs::Fs;
-use sdk_sync::git::{Commit, CommitHash};
-use sdk_sync::init_tracing;
-use sdk_sync::sync::gen::{GeneratedSdk, SdkGenerator};
-use sdk_sync::sync::{Sync, BOT_EMAIL, BOT_NAME, MODEL_STASH_BRANCH_NAME};
-use sdk_sync::versions::VersionsManifest;
-use std::path::{Path, PathBuf};
-use std::sync::Arc;
-
-static INIT_TRACING: Lazy<bool> = Lazy::new(|| {
-    init_tracing();
-    true
-});
-
-mockall::mock! {
-    CreateSdkGenerator {}
-    impl sdk_sync::sync::CreateSdkGenerator for CreateSdkGenerator {
-        fn create_sdk_generator(
-            &self,
-            aws_doc_sdk_examples_revision: &CommitHash,
-            examples_path: &Path,
-            fs: Arc<dyn Fs>,
-            reset_to_commit: Option<CommitHash>,
-            original_smithy_rs_path: &Path,
-        ) -> Result<Box<dyn SdkGenerator>>;
-    }
-}
-
-mockall::mock! {
-    Fs {}
-    impl sdk_sync::fs::Fs for Fs {
-        fn delete_all_generated_files_and_folders(&self, directory: &Path) -> Result<()>;
-        fn find_handwritten_files_and_folders(
-            &self,
-            aws_sdk_path: &Path,
-            build_artifacts_path: &Path,
-        ) -> Result<Vec<PathBuf>>;
-        fn remove_dir_all_idempotent(&self, path: &Path) -> Result<()>;
-        fn read_to_string(&self, path: &Path) -> Result<String>;
-        fn remove_file_idempotent(&self, path: &Path) -> Result<()>;
-        fn recursive_copy(&self, source: &Path, destination: &Path) -> Result<()>;
-    }
-}
-
-mockall::mock! {
-    Git {}
-    impl sdk_sync::git::Git for Git {
-        fn path(&self) -> &Path;
-        fn clone_to(&self, path: &Path) -> Result<()>;
-        fn get_head_revision(&self) -> Result<CommitHash>;
-        fn stage(&self, path: &Path) -> Result<()>;
-        fn commit_on_behalf(
-            &self,
-            bot_name: &str,
-            bot_email: &str,
-            author_name: &str,
-            author_email: &str,
-            message: &str,
-        ) -> Result<()>;
-        fn commit(&self, name: &str, email: &str, message: &str) -> Result<()>;
-        fn rev_list<'a>(
-            &self,
-            start_inclusive_revision: &str,
-            end_exclusive_revision: &str,
-            path: Option<&'a Path>,
-        ) -> Result<Vec<CommitHash>>;
-        fn show(&self, revision: &str) -> Result<Commit>;
-        fn hard_reset(&self, revision: &str) -> Result<()>;
-        fn current_branch_name(&self) -> Result<String>;
-        fn create_branch(&self, branch_name: &str, revision: &str) -> Result<()>;
-        fn fast_forward_merge(&self, branch_name: &str) -> Result<()>;
-        fn untracked_files(&self) -> Result<Vec<PathBuf>>;
-        fn changed_files(&self) -> Result<Vec<PathBuf>>;
-    }
-}
-
-mockall::mock! {
-    SdkGenerator {}
-    impl sdk_sync::sync::gen::SdkGenerator for SdkGenerator {
-        fn generate_sdk(&self) -> Result<GeneratedSdk>;
-    }
-}
-
-mockall::mock! {
-    Versions {}
-    impl sdk_sync::versions::Versions for Versions {
-        fn load(&self, aws_sdk_rust_path: &Path) -> Result<VersionsManifest>;
-    }
-}
-
-fn set_path(mock_git: &mut MockGit, path: &str) {
-    mock_git.expect_path().return_const(PathBuf::from(path));
-}
-
-fn expect_get_head_revision(repo: &mut MockGit, seq: &mut Sequence, head: &'static str) {
-    repo.expect_get_head_revision()
-        .once()
-        .in_sequence(seq)
-        .returning(move || Ok(CommitHash::from(head)));
-}
-
-fn expect_show_commit(repo: &mut MockGit, commit: Commit) {
-    let hash = commit.hash.as_ref().to_string();
-    repo.expect_show()
-        .withf(move |h| hash == h)
-        .once()
-        .returning(move |_| Ok(commit.clone()));
-}
-
-fn expect_hard_reset(repo: &mut MockGit, seq: &mut Sequence, hash: &str) {
-    let hash = hash.to_string();
-    repo.expect_hard_reset()
-        .withf(move |h| h == hash)
-        .once()
-        .in_sequence(seq)
-        .returning(|_| Ok(()));
-}
-
-fn expect_stage(repo: &mut MockGit, seq: &mut Sequence, path: &'static str) {
-    repo.expect_stage()
-        .withf(move |p| p.to_string_lossy() == path)
-        .once()
-        .in_sequence(seq)
-        .returning(|_| Ok(()));
-}
-
-fn expect_has_changes(repo: &mut MockGit, seq: &mut Sequence, changes: bool) {
-    repo.expect_untracked_files()
-        .once()
-        .in_sequence(seq)
-        .returning(move || Ok(Vec::new()));
-    repo.expect_changed_files()
-        .once()
-        .in_sequence(seq)
-        .returning(move || {
-            Ok(if changes {
-                vec![PathBuf::from("some-file")]
-            } else {
-                Vec::new()
-            })
-        });
-}
-
-#[derive(Default)]
-struct Mocks {
-    aws_doc_sdk_examples: MockGit,
-    aws_sdk_rust: MockGit,
-    smithy_rs: MockGit,
-    fs: MockFs,
-    versions: MockVersions,
-    create_sdk_generator: MockCreateSdkGenerator,
-}
-
-impl Mocks {
-    fn into_sync(self) -> Sync {
-        Sync::new_with(
-            self.aws_doc_sdk_examples,
-            self.aws_sdk_rust,
-            self.smithy_rs,
-            self.fs,
-            self.versions,
-            self.create_sdk_generator,
-        )
-    }
-
-    fn set_smithyrs_commits_to_sync(
-        &mut self,
-        previous_synced_commit: &'static str,
-        hashes: &'static [&'static str],
-    ) {
-        self.smithy_rs
-            .expect_rev_list()
-            .withf(move |begin, end, path| {
-                begin == "HEAD" && end == previous_synced_commit && path.is_none()
-            })
-            .once()
-            .returning(|_, _, _| Ok(hashes.iter().map(|&hash| CommitHash::from(hash)).collect()));
-    }
-
-    fn expect_recursive_copy(
-        &mut self,
-        seq: &mut Sequence,
-        source: &'static str,
-        dest: &'static str,
-    ) {
-        self.fs
-            .expect_recursive_copy()
-            .withf(move |src, dst| src.to_string_lossy() == source && dst.to_string_lossy() == dest)
-            .once()
-            .in_sequence(seq)
-            .returning(|_, _| Ok(()));
-    }
-
-    fn expect_delete_all_generated_files_and_folders(
-        &mut self,
-        seq: &mut Sequence,
-        sdk_path: &'static str,
-    ) {
-        self.fs
-            .expect_delete_all_generated_files_and_folders()
-            .withf(move |p| p.to_string_lossy() == sdk_path)
-            .once()
-            .in_sequence(seq)
-            .returning(|_| Ok(()));
-    }
-
-    fn expect_find_handwritten_files_and_folders(
-        &mut self,
-        seq: &mut Sequence,
-        sdk_path: &'static str,
-        artifacts_path: &'static str,
-        files: &'static [&'static str],
-    ) {
-        self.fs
-            .expect_find_handwritten_files_and_folders()
-            .withf(move |aws_sdk_p, artifacts_p| {
-                aws_sdk_p.to_string_lossy() == sdk_path
-                    && artifacts_p.to_string_lossy() == artifacts_path
-            })
-            .once()
-            .in_sequence(seq)
-            .returning(move |_, _| Ok(files.iter().map(PathBuf::from).collect()));
-    }
-}
-
-fn expect_copy_sdk(mocks: &mut Mocks) {
-    let mut seq = Sequence::new();
-    mocks.expect_delete_all_generated_files_and_folders(&mut seq, "/p2/aws-sdk-rust");
-    mocks.expect_find_handwritten_files_and_folders(
-        &mut seq,
-        "/p2/aws-sdk-rust",
-        "/p2/some-temp-cloned-smithy-rs/aws/sdk/build/aws-sdk",
-        &[], // no handwritten files found
-    );
-    mocks.expect_recursive_copy(
-        &mut seq,
-        "/p2/some-temp-cloned-smithy-rs/aws/sdk/build/aws-sdk/.",
-        "/p2/aws-sdk-rust",
-    );
-}
-
-fn expect_generate_sdk(
-    mocks: &mut Mocks,
-    expected_examples_revision: &'static str,
-    expected_examples_path: &'static str,
-    expected_reset_to_commit: Option<&str>,
-    expected_original_smithy_rs_path: &'static str,
-) {
-    let expected_reset_to_commit = expected_reset_to_commit.map(|c| CommitHash::from(c));
-    mocks
-        .create_sdk_generator
-        .expect_create_sdk_generator()
-        .withf(
-            move |examples_revision,
-                  examples_path,
-                  _fs,
-                  reset_to_commit,
-                  original_smithy_rs_path| {
-                examples_revision.as_ref() == expected_examples_revision
-                    && examples_path.to_string_lossy() == expected_examples_path
-                    && reset_to_commit == &expected_reset_to_commit
-                    && original_smithy_rs_path.to_string_lossy() == expected_original_smithy_rs_path
-            },
-        )
-        .once()
-        .returning(|_, _, _, _, _| {
-            let mut mock = MockSdkGenerator::new();
-            mock.expect_generate_sdk().once().returning(|| {
-                Ok(GeneratedSdk::new(
-                    "/p2/some-temp-cloned-smithy-rs/aws/sdk/build/aws-sdk",
-                ))
-            });
-            Ok(Box::new(mock))
-        });
-}
-
-fn expect_successful_smithyrs_sync(
-    mocks: &mut Mocks,
-    seq: &mut Sequence,
-    commit: Commit,
-    expected_commit_message: &str,
-) {
-    expect_show_commit(&mut mocks.smithy_rs, commit.clone());
-    expect_generate_sdk(
-        mocks,
-        "old-examples-hash",
-        "/p2/aws-sdk-rust/examples",
-        Some(commit.hash.as_ref()),
-        "/p2/smithy-rs",
-    );
-    expect_copy_sdk(mocks);
-
-    // Commit generated SDK
-    expect_has_changes(&mut mocks.aws_sdk_rust, seq, true);
-    expect_stage(&mut mocks.aws_sdk_rust, seq, ".");
-    let expected_commit_message = expected_commit_message.to_string();
-    mocks
-        .aws_sdk_rust
-        .expect_commit_on_behalf()
-        .withf(
-            move |bot_name, bot_email, author_name, author_email, message| {
-                bot_name == BOT_NAME
-                    && bot_email == BOT_EMAIL
-                    && author_name == commit.author_name
-                    && author_email == commit.author_email
-                    && message == expected_commit_message
-            },
-        )
-        .once()
-        .returning(|_, _, _, _, _| Ok(()));
-    mocks
-        .aws_sdk_rust
-        .expect_get_head_revision()
-        .once()
-        .returning(|| Ok(CommitHash::from("newly-synced-hash")));
-}
-
-fn expect_successful_example_sync(
-    mocks: &mut Mocks,
-    seq: &mut Sequence,
-    old_examples_hash: &'static str,
-    example_commits: &[Commit],
-) {
-    // Example revision discovery
-    {
-        let example_commits = example_commits.to_vec();
-        mocks
-            .aws_doc_sdk_examples
-            .expect_rev_list()
-            .withf(move |begin, end, path| {
-                begin == "HEAD"
-                    && end == old_examples_hash
-                    && *path == Some(&PathBuf::from("rust_dev_preview"))
-            })
-            .once()
-            .in_sequence(seq)
-            .returning(move |_, _, _| {
-                Ok(example_commits.iter().cloned().map(|c| c.hash).collect())
-            });
-    }
-
-    // Codegen
-    expect_generate_sdk(
-        mocks,
-        "hash2",
-        "/p2/aws-doc-sdk-examples/rust_dev_preview",
-        None,
-        "/p2/smithy-rs",
-    );
-    expect_copy_sdk(mocks);
-
-    // Commit generated SDK
-    expect_stage(&mut mocks.aws_sdk_rust, seq, ".");
-    for commit in example_commits {
-        expect_show_commit(&mut mocks.aws_doc_sdk_examples, commit.clone());
-    }
-    mocks
-        .aws_sdk_rust
-        .expect_commit()
-        .withf(|name, email, message| {
-            name == BOT_NAME
-                && email == BOT_EMAIL
-                && message.starts_with("[examples] Sync SDK examples")
-        })
-        .once()
-        .in_sequence(seq)
-        .returning(|_, _, _| Ok(()));
-}
-
-fn expect_model_changes(mocks: &mut Mocks, seq: &mut Sequence, models_changed: bool) {
-    let (old_hash, new_hash) = if models_changed {
-        ("with-new-models", "without-new-models")
-    } else {
-        ("no-new-models", "no-new-models")
-    };
-    expect_get_head_revision(&mut mocks.smithy_rs, seq, old_hash);
-
-    mocks
-        .smithy_rs
-        .expect_create_branch()
-        .with(eq(MODEL_STASH_BRANCH_NAME), eq("HEAD"))
-        .once()
-        .in_sequence(seq)
-        .returning(|_, _| Ok(()));
-
-    mocks
-        .smithy_rs
-        .expect_current_branch_name()
-        .once()
-        .in_sequence(seq)
-        .returning(|| Ok("main".to_string()));
-
-    expect_hard_reset(&mut mocks.smithy_rs, seq, "origin/main");
-    expect_get_head_revision(&mut mocks.smithy_rs, seq, new_hash);
-}
-
-fn expect_sync_model_changes(mocks: &mut Mocks, seq: &mut Sequence) {
-    // Expect merge of the models back into the main branch
-    mocks
-        .smithy_rs
-        .expect_fast_forward_merge()
-        .with(eq(MODEL_STASH_BRANCH_NAME))
-        .once()
-        .in_sequence(seq)
-        .returning(|_| Ok(()));
-
-    // HEAD has the model changes; set the commit info for it
-    expect_show_commit(
-        &mut mocks.smithy_rs,
-        Commit {
-            hash: "HEAD".into(),
-            author_name: BOT_NAME.into(),
-            author_email: BOT_EMAIL.into(),
-            message_subject: "Some model changes".into(),
-            message_body: "".into(),
-        },
-    );
-
-    // Codegen
-    expect_generate_sdk(
-        mocks,
-        "old-examples-hash",
-        "/p2/aws-sdk-rust/examples",
-        None,
-        "/p2/smithy-rs",
-    );
-    expect_copy_sdk(mocks);
-
-    // Commit generated SDK
-    expect_has_changes(&mut mocks.aws_sdk_rust, seq, true);
-    expect_stage(&mut mocks.aws_sdk_rust, seq, ".");
-    mocks
-        .aws_sdk_rust
-        .expect_commit()
-        .withf(|name, email, message| {
-            name == BOT_NAME && email == BOT_EMAIL && message.starts_with("Some model changes")
-        })
-        .once()
-        .in_sequence(seq)
-        .returning(|_, _, _| Ok(()));
-}
-
-#[test]
-fn mocked_e2e_without_model_changes() {
-    assert!(*INIT_TRACING);
-    let mut mocks = Mocks::default();
-    let mut seq = Sequence::new();
-
-    set_path(&mut mocks.aws_doc_sdk_examples, "/p2/aws-doc-sdk-examples");
-    set_path(&mut mocks.aws_sdk_rust, "/p2/aws-sdk-rust");
-    set_path(&mut mocks.smithy_rs, "/p2/smithy-rs");
-
-    mocks
-        .versions
-        .expect_load()
-        .withf(|p| p.to_string_lossy() == "/p2/aws-sdk-rust")
-        .once()
-        .returning(|_| {
-            Ok(VersionsManifest {
-                smithy_rs_revision: "some-previous-commit-hash".into(),
-                aws_doc_sdk_examples_revision: "old-examples-hash".into(),
-            })
-        });
-    mocks
-        .set_smithyrs_commits_to_sync("some-previous-commit-hash", &["hash-newest", "hash-oldest"]);
-
-    expect_model_changes(&mut mocks, &mut seq, false);
-
-    expect_successful_smithyrs_sync(
-        &mut mocks,
-        &mut seq,
-        Commit {
-            hash: "hash-oldest".into(),
-            author_name: "Some Dev".into(),
-            author_email: "somedev@example.com".into(),
-            message_subject: "Some commit subject".into(),
-            message_body: "".into(),
-        },
-        "[smithy-rs] Some commit subject",
-    );
-    expect_successful_smithyrs_sync(
-        &mut mocks,
-        &mut seq,
-        Commit {
-            hash: "hash-newest".into(),
-            author_name: "Another Dev".into(),
-            author_email: "anotherdev@example.com".into(),
-            message_subject: "Another commit subject".into(),
-            message_body: "This one has a body\n\n- bullet\n- bullet\n\nmore".into(),
-        },
-        "[smithy-rs] Another commit subject\n\nThis one has a body\n\n- bullet\n- bullet\n\nmore",
-    );
-
-    expect_successful_example_sync(
-        &mut mocks,
-        &mut seq,
-        "old-examples-hash",
-        &[
-            Commit {
-                hash: "hash2".into(),
-                author_name: "Some Example Writer".into(),
-                author_email: "someexamplewriter@example.com".into(),
-                message_subject: "More examples".into(),
-                message_body: "".into(),
-            },
-            Commit {
-                hash: "hash1".into(),
-                author_name: "Another Example Writer".into(),
-                author_email: "anotherexamplewriter@example.com".into(),
-                message_subject: "Another example".into(),
-                message_body: "This one has a body\n\n- bullet\n- bullet\n\nmore".into(),
-            },
-        ],
-    );
-
-    let sync = mocks.into_sync();
-    sync.sync().expect("success");
-}
-
-#[test]
-fn mocked_e2e_with_model_changes() {
-    assert!(*INIT_TRACING);
-    let mut mocks = Mocks::default();
-    let mut seq = Sequence::new();
-
-    set_path(&mut mocks.aws_doc_sdk_examples, "/p2/aws-doc-sdk-examples");
-    set_path(&mut mocks.aws_sdk_rust, "/p2/aws-sdk-rust");
-    set_path(&mut mocks.smithy_rs, "/p2/smithy-rs");
-
-    mocks
-        .versions
-        .expect_load()
-        .withf(|p| p.to_string_lossy() == "/p2/aws-sdk-rust")
-        .once()
-        .returning(|_| {
-            Ok(VersionsManifest {
-                smithy_rs_revision: "some-previous-commit-hash".into(),
-                aws_doc_sdk_examples_revision: "old-examples-hash".into(),
-            })
-        });
-    mocks
-        .set_smithyrs_commits_to_sync("some-previous-commit-hash", &["hash-newest", "hash-oldest"]);
-
-    expect_model_changes(&mut mocks, &mut seq, true);
-
-    expect_successful_smithyrs_sync(
-        &mut mocks,
-        &mut seq,
-        Commit {
-            hash: "hash-oldest".into(),
-            author_name: "Some Dev".into(),
-            author_email: "somedev@example.com".into(),
-            message_subject: "Some commit subject".into(),
-            message_body: "".into(),
-        },
-        "[smithy-rs] Some commit subject",
-    );
-    expect_successful_smithyrs_sync(
-        &mut mocks,
-        &mut seq,
-        Commit {
-            hash: "hash-newest".into(),
-            author_name: "Another Dev".into(),
-            author_email: "anotherdev@example.com".into(),
-            message_subject: "Another commit subject".into(),
-            message_body: "This one has a body\n\n- bullet\n- bullet\n\nmore".into(),
-        },
-        "[smithy-rs] Another commit subject\n\nThis one has a body\n\n- bullet\n- bullet\n\nmore",
-    );
-
-    expect_sync_model_changes(&mut mocks, &mut seq);
-
-    expect_successful_example_sync(
-        &mut mocks,
-        &mut seq,
-        "old-examples-hash",
-        &[
-            Commit {
-                hash: "hash2".into(),
-                author_name: "Some Example Writer".into(),
-                author_email: "someexamplewriter@example.com".into(),
-                message_subject: "More examples".into(),
-                message_body: "".into(),
-            },
-            Commit {
-                hash: "hash1".into(),
-                author_name: "Another Example Writer".into(),
-                author_email: "anotherexamplewriter@example.com".into(),
-                message_subject: "Another example".into(),
-                message_body: "This one has a body\n\n- bullet\n- bullet\n\nmore".into(),
-            },
-        ],
-    );
-
-    let sync = mocks.into_sync();
-    sync.sync().expect("success");
-}
-- 
GitLab