chore: Revert "test: Git integration tests (#38237)" (#38333)

This commit is contained in:
Nidhi 2024-12-24 01:50:45 +05:30 committed by GitHub
parent 5855115abb
commit bc38ee739e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 6 additions and 1040 deletions

View File

@ -13,8 +13,6 @@ public interface ArtifactCE {
String getName();
void setName(String artifactName);
String getWorkspaceId();
Boolean getExportWithConfiguration();

View File

@ -2187,17 +2187,13 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE {
.onErrorResume(throwable -> {
log.error("Delete branch failed {}", throwable.getMessage());
if (throwable instanceof CannotDeleteCurrentBranchException) {
return releaseFileLock(baseArtifactId)
.then(Mono.error(new AppsmithException(
return Mono.error(new AppsmithException(
AppsmithError.GIT_ACTION_FAILED,
"delete branch",
"Cannot delete current checked out branch")));
"Cannot delete current checked out branch"));
}
return releaseFileLock(baseArtifactId)
.then(Mono.error(new AppsmithException(
AppsmithError.GIT_ACTION_FAILED,
"delete branch",
throwable.getMessage())));
return Mono.error(new AppsmithException(
AppsmithError.GIT_ACTION_FAILED, "delete branch", throwable.getMessage()));
})
.flatMap(isBranchDeleted ->
releaseFileLock(baseArtifactId).map(status -> isBranchDeleted))

View File

@ -1,584 +0,0 @@
package com.appsmith.server.git;
import com.appsmith.external.dtos.GitBranchDTO;
import com.appsmith.external.dtos.GitStatusDTO;
import com.appsmith.external.dtos.MergeStatusDTO;
import com.appsmith.git.configurations.GitServiceConfig;
import com.appsmith.server.applications.base.ApplicationService;
import com.appsmith.server.configurations.ProjectProperties;
import com.appsmith.server.constants.ArtifactType;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.constants.GitDefaultCommitMessage;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Artifact;
import com.appsmith.server.domains.GitArtifactMetadata;
import com.appsmith.server.domains.GitProfile;
import com.appsmith.server.dtos.AutoCommitResponseDTO;
import com.appsmith.server.dtos.GitCommitDTO;
import com.appsmith.server.dtos.GitConnectDTO;
import com.appsmith.server.dtos.GitMergeDTO;
import com.appsmith.server.dtos.GitPullDTO;
import com.appsmith.server.git.autocommit.AutoCommitService;
import com.appsmith.server.git.common.CommonGitService;
import com.appsmith.server.git.resolver.GitArtifactHelperResolver;
import com.appsmith.server.git.templates.contexts.GitContext;
import com.appsmith.server.git.templates.providers.GitBranchesTestTemplateProvider;
import com.appsmith.server.services.GitArtifactHelper;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.Status;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithUserDetails;
import org.testcontainers.junit.jupiter.Testcontainers;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.List;
import static com.appsmith.external.git.constants.GitConstants.DEFAULT_COMMIT_MESSAGE;
import static com.appsmith.external.git.constants.GitConstants.EMPTY_COMMIT_ERROR_MESSAGE;
import static com.appsmith.server.exceptions.AppsmithError.GIT_MERGE_FAILED_LOCAL_CHANGES;
import static com.appsmith.server.git.autocommit.AutoCommitEventHandlerImpl.AUTO_COMMIT_MSG_FORMAT;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
/**
* This integration test suite validates the end-to-end Git workflow for artifacts, performing a sequence of
* operations that test repository setup, branch management, status validation, and cleanup. The operations
* proceed as follows:
*
* 1. **Connect Artifact to Git**:
* - The artifact is connected to an empty Git repository using a remote URL provided by the Git server initializer.
* - A system-generated commit is created as part of the connection process.
* - Auto-commit is enabled by default, as verified in the artifact metadata.
* - The repository is checked to confirm a single system-generated commit and a clean working directory.
*
* 2. **Verify Initial Repository State**:
* - The default branch is initialized, and its name is verified to match the metadata.
* - The repository status is confirmed to be clean with no uncommitted changes.
*
* 3. **Trigger and Validate Auto-Commit**:
* - Auto-commit is triggered, and the resulting commit is validated in the Git log.
* - Commit history is checked to confirm the auto-commit appears as a second commit following the initial system-generated commit.
*
* 4. **Perform Status, Pull, and Commit Operations on the Default Branch (`master`)**:
* - The repository status is checked to confirm no changes (`isClean = true`).
* - A `pull` operation is executed to ensure synchronization, even when no updates are available.
* - A `commit` is attempted with no changes, and the response is validated to confirm no new commits were created.
*
* 5. **Create and Verify Branches**:
* - A new branch `foo` is created from the default branch (`master`).
* - Metadata for `foo` is validated, and the commit history confirms that `foo` starts from the latest commit on `master`.
* - A second branch `bar` is created from `foo`. Its metadata is verified, and the commit log confirms it starts from the latest commit on `foo`.
*
* 6. **Test Merging Scenarios**:
* - A merge from `bar` to `foo` is validated and shows no action required (`ALREADY_UP_TO_DATE`), as no changes exist.
* - Additional changes made to `bar` are merged back into `foo` successfully.
*
* 7. **Branch Deletion and Repopulation**:
* - The branch `foo` is deleted locally but repopulated from the remote repository.
* - The latest commit on `foo` is verified to match the changes made on `foo` before deletion.
* - An attempt to delete the currently checked-out branch (`master`) fails as expected.
*
* 8. **Make Changes and Validate Commits**:
* - Changes are made to the artifact on `foo` to trigger diffs.
* - The repository status is validated as `isClean = false` with pending changes.
* - A commit is created with a custom message, and the Git log confirms the commit as the latest on `foo`.
* - Changes are successfully discarded, restoring the repository to a clean state.
*
* 9. **Set and Test Branch Protection**:
* - The `master` branch is marked as protected. Commits directly to `master` are restricted.
* - Attempts to commit to `master` fail with the appropriate error message.
*
* 10. **Merge Branches (`baz` to `bar`)**:
* - A new branch `baz` is created from `bar`, and its commit log is verified.
* - Changes are made to `baz` and successfully merged into `bar` via a fast-forward merge.
* - The commit history confirms the merge, and the top commit matches the changes made in `baz`.
*
* 11. **Disconnect Artifact and Cleanup**:
* - The artifact is disconnected from the Git repository.
* - All repository branches (`foo`, `bar`, `baz`) except `master` are removed.
* - The file system is verified to confirm all repository data is cleaned up.
* - Applications associated with the deleted branches are also removed.
*
* This test suite ensures comprehensive coverage of Git workflows, including repository connection, branch creation,
* branch protection, merging, status validation, and repository cleanup. Each operation includes detailed assertions
* to validate expected outcomes and handle edge cases.
*/
@Testcontainers
@SpringBootTest
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class GitBranchesIT {
@Autowired
@RegisterExtension
GitBranchesTestTemplateProvider templateProvider;
@Autowired
@RegisterExtension
ArtifactBuilderExtension artifactBuilderExtension;
@Autowired
@RegisterExtension
GitServerInitializerExtension gitServerInitializerExtension;
@Autowired
CommonGitService commonGitService;
@Autowired
GitTestUtils gitTestUtils;
@Autowired
GitArtifactHelperResolver gitArtifactHelperResolver;
@Autowired
GitServiceConfig gitServiceConfig;
@Autowired
AutoCommitService autoCommitService;
@Autowired
ProjectProperties projectProperties;
@Autowired
ApplicationService applicationService;
final String ORIGIN = "https://foo.bar.com";
@TestTemplate
@WithUserDetails(value = "api_user")
void test(GitContext gitContext, ExtensionContext extensionContext) throws IOException, GitAPIException, InterruptedException {
ExtensionContext.Store contextStore = extensionContext.getStore(ExtensionContext.Namespace.create(ArtifactBuilderExtension.class));
String artifactId = contextStore.get(FieldName.ARTIFACT_ID, String.class);
GitConnectDTO connectDTO = new GitConnectDTO();
connectDTO.setRemoteUrl(gitServerInitializerExtension.getGitSshUrl("test" + artifactId));
GitProfile gitProfile = new GitProfile("foo bar", "foo@bar.com", null);
connectDTO.setGitProfile(gitProfile);
// TODO:
// - Move the filePath variable to be relative, so that template name and repo name is prettier
// - Is it possible to use controller layer here? Might help with also including web filters in IT
Artifact artifact = commonGitService.connectArtifactToGit(artifactId, connectDTO, ORIGIN, gitContext.getArtifactType())
.block();
assertThat(artifact).isNotNull();
ArtifactType artifactType = artifact.getArtifactType();
GitArtifactMetadata artifactMetadata = artifact.getGitArtifactMetadata();
GitArtifactHelper<?> artifactHelper = gitArtifactHelperResolver.getArtifactHelper(artifactType);
Path repoSuffix = artifactHelper.getRepoSuffixPath(
artifact.getWorkspaceId(),
artifactMetadata.getDefaultArtifactId(),
artifactMetadata.getRepoName());
// Auto-commit should be turned on by default
assertThat(artifactMetadata.getAutoCommitConfig().getEnabled()).isTrue();
Path path = Path.of(gitServiceConfig.getGitRootPath()).resolve(repoSuffix);
String branch;
ObjectId topOfCommits;
try (Git git = Git.open(path.toFile())) {
branch = git.log().getRepository().getBranch();
assertThat(branch).isEqualTo(artifactMetadata.getBranchName());
// Assert only single system generated commit exists on FS
Iterable<RevCommit> commits = git.log().call();
Iterator<RevCommit> commitIterator = commits.iterator();
assertThat(commitIterator.hasNext()).isTrue();
RevCommit firstCommit = commitIterator.next();
assertThat(firstCommit.getFullMessage()).isEqualTo(DEFAULT_COMMIT_MESSAGE + GitDefaultCommitMessage.CONNECT_FLOW.getReason());
topOfCommits = firstCommit.getId();
assertThat(commitIterator.hasNext()).isFalse();
// Assert that git directory is clean
Status status = git.status().call();
assertThat(status.isClean()).isTrue();
}
// Assert that the artifact does have auto-commit requirements, and auto-commit gets initiated
AutoCommitResponseDTO autoCommitResponseDTO = autoCommitService.autoCommitApplication(artifactId).block();
assertThat(autoCommitResponseDTO).isNotNull();
AutoCommitResponseDTO.AutoCommitResponse autoCommitProgress = autoCommitResponseDTO.getAutoCommitResponse();
assertThat(autoCommitProgress).isEqualTo(AutoCommitResponseDTO.AutoCommitResponse.PUBLISHED);
// Wait for auto-commit to complete
// This should not take more than 2 seconds, we're checking every 500 ms
long startTime = System.currentTimeMillis(), currentTime = System.currentTimeMillis();
while (!autoCommitProgress.equals(AutoCommitResponseDTO.AutoCommitResponse.IDLE)) {
Thread.sleep(500);
if (currentTime - startTime > 2000) {
fail("Auto-commit took too long");
}
autoCommitProgress = getAutocommitProgress(artifactId, artifact, artifactMetadata);
currentTime = System.currentTimeMillis();
}
// Now there should be two commits in the git log response
try (Git git = Git.open(path.toFile())) {
branch = git.log().getRepository().getBranch();
assertThat(branch).isEqualTo(artifactMetadata.getBranchName());
Iterable<RevCommit> commits = git.log().call();
Iterator<RevCommit> commitIterator = commits.iterator();
assertThat(commitIterator.hasNext()).isTrue();
RevCommit autoCommit = commitIterator.next();
assertThat(autoCommit.getFullMessage()).isEqualTo(String.format(AUTO_COMMIT_MSG_FORMAT, projectProperties.getVersion()));
assertThat(commitIterator.hasNext()).isTrue();
RevCommit firstCommit = commitIterator.next();
assertThat(firstCommit.getId()).isEqualTo(topOfCommits);
topOfCommits = autoCommit.getId();
}
// Assert that the initialized branch is set as default
assertThat(artifactMetadata.getBranchName()).isEqualTo(artifactMetadata.getDefaultBranchName());
// Assert that the branch is not protected by default
assertThat(artifactMetadata.getBranchProtectionRules()).isNullOrEmpty();
// Check that the status is clean
GitStatusDTO statusDTO = commonGitService.getStatus(artifactId, true, artifactType).block();
assertThat(statusDTO).isNotNull();
assertThat(statusDTO.getIsClean()).isTrue();
assertThat(statusDTO.getAheadCount()).isEqualTo(0);
assertThat(statusDTO.getBehindCount()).isEqualTo(0);
// Check that pull when not required, still goes through
GitPullDTO gitPullDTO = commonGitService.pullArtifact(artifactId, artifactType).block();
assertThat(gitPullDTO).isNotNull();
// Check that commit says that there is nothing to commit
GitCommitDTO commitDTO = new GitCommitDTO();
commitDTO.setCommitMessage("Unused message");
commitDTO.setDoPush(false);
String commitResponse = commonGitService.commitArtifact(commitDTO, artifactId, artifactType)
.block();
assertThat(commitResponse).contains(EMPTY_COMMIT_ERROR_MESSAGE);
// Check that the previous attempt didn't actually go through
try (Git git = Git.open(path.toFile())) {
branch = git.log().getRepository().getBranch();
assertThat(branch).isEqualTo(artifactMetadata.getBranchName());
Iterable<RevCommit> commits = git.log().call();
assertThat(commits.iterator().next().getId()).isEqualTo(topOfCommits);
}
// Check that discard, even when not required, goes through
Artifact discardedArtifact = commonGitService.discardChanges(artifactId, artifactType).block();
assertThat(discardedArtifact).isNotNull();
// Make a change in the artifact to trigger a diff
gitTestUtils.createADiffInArtifact(artifact).block();
// Check that the status is not clean
GitStatusDTO statusDTO2 = commonGitService.getStatus(artifactId, true, artifactType).block();
assertThat(statusDTO2).isNotNull();
assertThat(statusDTO2.getIsClean()).isFalse();
assertThat(statusDTO2.getAheadCount()).isEqualTo(0);
assertThat(statusDTO2.getBehindCount()).isEqualTo(0);
// Check that commit makes the custom message be the top of the log
GitCommitDTO commitDTO2 = new GitCommitDTO();
commitDTO2.setCommitMessage("Custom message");
commitDTO2.setDoPush(true);
String commitResponse2 = commonGitService.commitArtifact(commitDTO2, artifactId, artifactType)
.block();
assertThat(commitResponse2).contains("Committed successfully!");
try (Git git = Git.open(path.toFile())) {
branch = git.log().getRepository().getBranch();
assertThat(branch).isEqualTo(artifactMetadata.getBranchName());
Iterable<RevCommit> commits = git.log().call();
Iterator<RevCommit> commitIterator = commits.iterator();
RevCommit newCommit = commitIterator.next();
assertThat(newCommit.getFullMessage()).isEqualTo("Custom message");
assertThat(commitIterator.next().getId()).isEqualTo(topOfCommits);
topOfCommits = newCommit.getId();
}
// Check that status is clean again
GitStatusDTO statusDTO3 = commonGitService.getStatus(artifactId, true, artifactType).block();
assertThat(statusDTO3).isNotNull();
assertThat(statusDTO3.getIsClean()).isTrue();
assertThat(statusDTO3.getAheadCount()).isEqualTo(0);
assertThat(statusDTO3.getBehindCount()).isEqualTo(0);
// Make another change to trigger a diff
gitTestUtils.createADiffInArtifact(artifact).block();
// Check that status in not clean
GitStatusDTO statusDTO4 = commonGitService.getStatus(artifactId, true, artifactType).block();
assertThat(statusDTO4).isNotNull();
assertThat(statusDTO4.getIsClean()).isFalse();
assertThat(statusDTO4.getAheadCount()).isEqualTo(0);
assertThat(statusDTO4.getBehindCount()).isEqualTo(0);
// Protect the master branch
List<String> protectedBranches = commonGitService.updateProtectedBranches(artifactId, List.of(branch), artifactType).block();
assertThat(protectedBranches).containsExactly(branch);
// Now try to commit, and check that it fails
GitCommitDTO commitDTO3 = new GitCommitDTO();
commitDTO3.setCommitMessage("Failed commit");
commitDTO3.setDoPush(false);
Mono<String> commitResponse3Mono = commonGitService.commitArtifact(commitDTO3, artifactId, artifactType);
StepVerifier.create(commitResponse3Mono)
.expectErrorSatisfies(e -> assertThat(e.getMessage()).contains("Cannot commit to protected branch"))
.verify();
// Create a new branch foo from master, check that the commit for new branch is created as system generated
// On top of the previous custom commit
GitBranchDTO fooBranchDTO = new GitBranchDTO();
fooBranchDTO.setBranchName("foo");
Artifact fooArtifact = commonGitService.createBranch(artifactId, fooBranchDTO, artifactType).block();
assertThat(fooArtifact).isNotNull();
String fooArtifactId = fooArtifact.getId();
GitArtifactMetadata fooMetadata = fooArtifact.getGitArtifactMetadata();
assertThat(fooMetadata.getBranchName()).isEqualTo("foo");
try (Git git = Git.open(path.toFile())) {
branch = git.log().getRepository().getBranch();
assertThat(branch).isEqualTo(fooMetadata.getBranchName());
Iterable<RevCommit> commits = git.log().call();
Iterator<RevCommit> commitIterator = commits.iterator();
RevCommit newCommit = commitIterator.next();
assertThat(newCommit.getFullMessage()).contains("branch: foo");
assertThat(commitIterator.next().getId()).isEqualTo(topOfCommits);
topOfCommits = newCommit.getId();
}
// Check that status on foo is clean again
GitStatusDTO statusDTO5 = commonGitService.getStatus(fooArtifactId, true, artifactType).block();
assertThat(statusDTO5).isNotNull();
assertThat(statusDTO5.getIsClean()).isTrue();
assertThat(statusDTO5.getAheadCount()).isEqualTo(0);
assertThat(statusDTO5.getBehindCount()).isEqualTo(0);
// Create another branch bar from foo
GitBranchDTO barBranchDTO = new GitBranchDTO();
barBranchDTO.setBranchName("bar");
Artifact barArtifact = commonGitService.createBranch(fooArtifactId, barBranchDTO, artifactType).block();
assertThat(barArtifact).isNotNull();
String barArtifactId = barArtifact.getId();
GitArtifactMetadata barMetadata = barArtifact.getGitArtifactMetadata();
assertThat(barMetadata.getBranchName()).isEqualTo("bar");
try (Git git = Git.open(path.toFile())) {
branch = git.log().getRepository().getBranch();
assertThat(branch).isEqualTo(barMetadata.getBranchName());
Iterable<RevCommit> commits = git.log().call();
Iterator<RevCommit> commitIterator = commits.iterator();
assertThat(commitIterator.next().getId()).isEqualTo(topOfCommits);
}
// Check merge status to foo shows no action required
// bar -> foo
GitMergeDTO gitMergeDTO = new GitMergeDTO();
gitMergeDTO.setDestinationBranch("foo");
gitMergeDTO.setSourceBranch("bar");
MergeStatusDTO mergeStatusDTO = commonGitService.isBranchMergeable(barArtifactId, gitMergeDTO, artifactType).block();
assertThat(mergeStatusDTO).isNotNull();
assertThat(mergeStatusDTO.getStatus()).isEqualTo("ALREADY_UP_TO_DATE");
// Delete foo locally and re-populate from remote
List<String> branchList = commonGitService.listBranchForArtifact(artifactId, false, artifactType)
.flatMapMany(Flux::fromIterable)
.map(GitBranchDTO::getBranchName)
.collectList()
.block();
assertThat(branchList).containsExactlyInAnyOrder(
artifactMetadata.getBranchName(),
"origin/" + artifactMetadata.getBranchName(),
fooMetadata.getBranchName(),
"origin/" + fooMetadata.getBranchName(),
barMetadata.getBranchName(),
"origin/" + barMetadata.getBranchName());
Mono<? extends Artifact> deleteBranchAttemptMono = commonGitService.deleteBranch(artifactId, "foo", artifactType);
StepVerifier
.create(deleteBranchAttemptMono)
.expectErrorSatisfies(e -> assertThat(e.getMessage()).contains("Cannot delete current checked out branch"))
.verify();
// TODO: I'm having to checkout myself to be able to delete the branch.
// Are we relying on auto-commit check to do this otherwise?
// Is this a potential bug?
try (Git git = Git.open(path.toFile())) {
git.checkout().setName("bar").call();
}
commonGitService.deleteBranch(artifactId, "foo", artifactType).block();
List<String> branchList2 = commonGitService.listBranchForArtifact(artifactId, false, artifactType)
.flatMapMany(Flux::fromIterable)
.map(GitBranchDTO::getBranchName)
.collectList()
.block();
assertThat(branchList2).containsExactlyInAnyOrder(
artifactMetadata.getBranchName(),
"origin/" + artifactMetadata.getBranchName(),
"origin/" + fooMetadata.getBranchName(),
barMetadata.getBranchName(),
"origin/" + barMetadata.getBranchName());
Artifact checkedOutFooArtifact = commonGitService.checkoutBranch(artifactId, "origin/foo", true, artifactType).block();
assertThat(checkedOutFooArtifact).isNotNull();
List<String> branchList3 = commonGitService.listBranchForArtifact(artifactId, false, artifactType)
.flatMapMany(Flux::fromIterable)
.map(GitBranchDTO::getBranchName)
.collectList()
.block();
assertThat(branchList3).containsExactlyInAnyOrder(
artifactMetadata.getBranchName(),
"origin/" + artifactMetadata.getBranchName(),
fooMetadata.getBranchName(),
"origin/" + fooMetadata.getBranchName(),
barMetadata.getBranchName(),
"origin/" + barMetadata.getBranchName());
// Verify latest commit on foo should be same as changes made on foo previously
try (Git git = Git.open(path.toFile())) {
branch = git.log().getRepository().getBranch();
assertThat(branch).isEqualTo(fooMetadata.getBranchName());
Iterable<RevCommit> commits = git.log().call();
Iterator<RevCommit> commitIterator = commits.iterator();
assertThat(commitIterator.next().getId()).isEqualTo(topOfCommits);
}
// Make more changes on foo and attempt discard
gitTestUtils.createADiffInArtifact(checkedOutFooArtifact).block();
GitStatusDTO discardableStatus = commonGitService.getStatus(checkedOutFooArtifact.getId(), false, artifactType).block();
assertThat(discardableStatus).isNotNull();
assertThat(discardableStatus.getIsClean()).isFalse();
Artifact discardedFoo = commonGitService.discardChanges(checkedOutFooArtifact.getId(), artifactType).block();
GitStatusDTO discardedStatus = commonGitService.getStatus(checkedOutFooArtifact.getId(), false, artifactType).block();
assertThat(discardedStatus).isNotNull();
// TODO: Why is this not clean?
// There is an on page load that gets triggered here that is causing a diff
// This should ideally have already been fixed on initial artifact import
// assertThat(discardedStatus.getIsClean()).isTrue();
// Make a change to trigger a diff on bar
gitTestUtils.createADiffInArtifact(barArtifact).block();
// Check merge status to master shows not merge-able
GitMergeDTO gitMergeDTO2 = new GitMergeDTO();
gitMergeDTO2.setSourceBranch("bar");
gitMergeDTO2.setDestinationBranch("master");
MergeStatusDTO mergeStatusDTO2 = commonGitService.isBranchMergeable(barArtifactId, gitMergeDTO2, artifactType).block();
assertThat(mergeStatusDTO2).isNotNull();
assertThat(mergeStatusDTO2.isMergeAble()).isFalse();
assertThat(mergeStatusDTO2.getMessage()).isEqualTo(GIT_MERGE_FAILED_LOCAL_CHANGES.getMessage("bar"));
// Create a new branch baz and check for new commit
GitBranchDTO gitBranchDTO = new GitBranchDTO();
gitBranchDTO.setBranchName("baz");
Artifact bazArtifact = commonGitService.createBranch(barArtifactId, gitBranchDTO, artifactType).block();
assertThat(bazArtifact).isNotNull();
try (Git git = Git.open(path.toFile())) {
Iterable<RevCommit> commits = git.log().call();
Iterator<RevCommit> commitIterator = commits.iterator();
RevCommit newCommit = commitIterator.next();
assertThat(newCommit.getFullMessage()).contains("branch: baz");
assertThat(commitIterator.next().getId()).isEqualTo(topOfCommits);
topOfCommits = newCommit.getId();
}
// TODO: We're having to discard on bar because
// create branch today retains uncommitted change on source branch as well
// We will need to update this line once that is fixed.
// It won't get caught in tests otherwise since this discard would be a redundant op
commonGitService.discardChanges(barArtifactId, artifactType).block();
GitMergeDTO gitMergeDTO3 = new GitMergeDTO();
gitMergeDTO3.setSourceBranch("baz");
gitMergeDTO3.setDestinationBranch("bar");
MergeStatusDTO mergeStatusDTO3 = commonGitService.isBranchMergeable(barArtifactId, gitMergeDTO3, artifactType).block();
assertThat(mergeStatusDTO3).isNotNull();
assertThat(mergeStatusDTO3.isMergeAble()).isTrue();
// Merge bar to master and check log of commits on foo is same as bar
MergeStatusDTO barToBazMergeStatus = commonGitService.mergeBranch(barArtifactId, gitMergeDTO3, artifactType).block();
assertThat(barToBazMergeStatus).isNotNull();
assertThat(barToBazMergeStatus.isMergeAble()).isTrue();
assertThat(barToBazMergeStatus.getStatus()).contains("FAST_FORWARD");
// Since fast-forward should succeed here, top of commit should not change
try (Git git = Git.open(path.toFile())) {
Iterable<RevCommit> commits = git.log().call();
Iterator<RevCommit> commitIterator = commits.iterator();
assertThat(commitIterator.next().getId()).isEqualTo(topOfCommits);
}
// Disconnect artifact and verify non-existence of `foo`, `bar` and `baz`
Artifact disconnectedArtifact = commonGitService.detachRemote(artifactId, artifactType).block();
assertThat(disconnectedArtifact).isNotNull();
assertThat(disconnectedArtifact.getGitArtifactMetadata()).isNull();
// TODO: This needs to be generified for artifacts
Application deletedFooArtifact = applicationService.findById(checkedOutFooArtifact.getId()).block();
assertThat(deletedFooArtifact).isNull();
Application deletedBarArtifact = applicationService.findById(barArtifactId).block();
assertThat(deletedBarArtifact).isNull();
Application deletedBazArtifact = applicationService.findById(bazArtifact.getId()).block();
assertThat(deletedBazArtifact).isNull();
Application existingMasterArtifact = applicationService.findById(artifactId).block();
assertThat(existingMasterArtifact).isNotNull();
// Verify FS is clean after disconnect
boolean repoDirectoryNotExists = Files.notExists(path);
assertThat(repoDirectoryNotExists).isTrue();
}
private AutoCommitResponseDTO.AutoCommitResponse getAutocommitProgress(String artifactId, Artifact artifact, GitArtifactMetadata artifactMetadata) {
AutoCommitResponseDTO autoCommitProgress = commonGitService.getAutoCommitProgress(artifactId, artifactMetadata.getBranchName(), artifact.getArtifactType()).block();
assertThat(autoCommitProgress).isNotNull();
return autoCommitProgress.getAutoCommitResponse();
}
}

View File

@ -1,69 +0,0 @@
package com.appsmith.server.git.templates.contexts;
import com.appsmith.server.constants.ArtifactType;
import com.appsmith.server.dtos.ArtifactExchangeJson;
import com.appsmith.server.git.ArtifactBuilderExtension;
import org.junit.jupiter.api.extension.Extension;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
import java.util.List;
public class GitContext implements TestTemplateInvocationContext, ParameterResolver {
private final String fileName;
private final Class<? extends ArtifactExchangeJson> artifactExchangeJsonType;
private final ArtifactType artifactType;
public GitContext(
ExtensionContext extensionContext, String fileName, Class<? extends ArtifactExchangeJson> artifactExchangeJsonType, ArtifactType artifactType) {
this.artifactType = artifactType;
ExtensionContext.Store contextStore = extensionContext.getStore(ExtensionContext.Namespace.create(ArtifactBuilderExtension.class));
contextStore.put(ArtifactExchangeJson.class, artifactExchangeJsonType);
contextStore.put("filePath", fileName);
this.fileName = fileName;
this.artifactExchangeJsonType = artifactExchangeJsonType;
}
@Override
public String getDisplayName(int invocationIndex) {
return fileName;
}
@Override
public List<Extension> getAdditionalExtensions() {
return List.of(this);
}
public String getFileName() {
return fileName;
}
public Class<? extends ArtifactExchangeJson> getArtifactExchangeJsonType() {
return artifactExchangeJsonType;
}
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
return true;
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
if (parameterContext.getParameter().getType().equals(ExtensionContext.class)) {
return extensionContext;
}
return this;
}
public ArtifactType getArtifactType() {
return artifactType;
}
}

View File

@ -1,7 +0,0 @@
package com.appsmith.server.git.templates.providers;
import com.appsmith.server.git.templates.providers.ce.GitBranchesTestTemplateProviderCE;
import org.springframework.stereotype.Component;
@Component
public class GitBranchesTestTemplateProvider extends GitBranchesTestTemplateProviderCE {}

View File

@ -1,31 +0,0 @@
package com.appsmith.server.git.templates.providers.ce;
import com.appsmith.server.constants.ArtifactType;
import com.appsmith.server.dtos.ApplicationJson;
import com.appsmith.server.git.templates.contexts.GitContext;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContext;
import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
public class GitBranchesTestTemplateProviderCE implements TestTemplateInvocationContextProvider {
@Override
public boolean supportsTestTemplate(ExtensionContext extensionContext) {
return true;
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
ExtensionContext extensionContext) {
GitContext context = new GitContext(
extensionContext,
"com/appsmith/server/git/application.json",
ApplicationJson.class,
ArtifactType.APPLICATION);
return Stream.of(context);
}
}

View File

@ -1,22 +0,0 @@
package com.appsmith.server.git;
import com.appsmith.server.constants.ArtifactType;
import com.appsmith.server.dtos.ArtifactExchangeJson;
import org.junit.jupiter.api.extension.ExtensionContext;
public interface ArtifactBuilderContext extends ExtensionContext {
ArtifactType getArtifactType();
Class<? extends ArtifactExchangeJson> getArtifactJsonType();
String getArtifactJsonPath();
String getWorkspaceId();
void setWorkspaceId(String workspaceId);
String getArtifactId();
void setArtifactId(String artifactId);
}

View File

@ -1,117 +0,0 @@
package com.appsmith.server.git;
import com.appsmith.server.applications.base.ApplicationService;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Artifact;
import com.appsmith.server.domains.User;
import com.appsmith.server.domains.Workspace;
import com.appsmith.server.dtos.ArtifactExchangeJson;
import com.appsmith.server.imports.internal.ImportService;
import com.appsmith.server.migrations.JsonSchemaMigration;
import com.appsmith.server.services.ApplicationPageService;
import com.appsmith.server.services.UserService;
import com.appsmith.server.services.WorkspaceService;
import com.appsmith.server.solutions.ApplicationPermission;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
/**
* This extension basically just creates a new workspace and initializes the artifact provided in context
* This artifact is provided in the form of a JSON file that is specified in the context itself
*/
@Component
public class ArtifactBuilderExtension implements AfterEachCallback, BeforeEachCallback {
@Autowired
UserService userService;
@Autowired
WorkspaceService workspaceService;
@Autowired
ImportService importService;
@Autowired
ObjectMapper objectMapper;
@Autowired
JsonSchemaMigration jsonSchemaMigration;
@Autowired
ApplicationService applicationService;
@Autowired
ApplicationPermission applicationPermission;
@Autowired
ApplicationPageService applicationPageService;
@Override
public void beforeEach(ExtensionContext extensionContext) throws Exception {
ExtensionContext.Store parentContextStore = extensionContext.getParent().get().getStore(ExtensionContext.Namespace.create(ArtifactBuilderExtension.class));
Class<? extends ArtifactExchangeJson> aClass = parentContextStore.get(ArtifactExchangeJson.class, Class.class);
String filePath = parentContextStore.get("filePath", String.class);
ExtensionContext.Store contextStore = extensionContext.getStore(ExtensionContext.Namespace.create(ArtifactBuilderExtension.class));
ArtifactExchangeJson artifactExchangeJson = createArtifactJson(filePath, aClass).block();
assertThat(artifactExchangeJson).isNotNull();
artifactExchangeJson.getArtifact().setName(aClass.getSimpleName() + "_" + UUID.randomUUID());
User apiUser = userService.findByEmail("api_user").block();
Workspace toCreate = new Workspace();
toCreate.setName("Workspace_" + UUID.randomUUID());
Workspace workspace =
workspaceService.create(toCreate, apiUser, Boolean.FALSE).block();
assertThat(workspace).isNotNull();
Artifact artifact = importService.importNewArtifactInWorkspaceFromJson(workspace.getId(), artifactExchangeJson).block();
assertThat(artifact).isNotNull();
contextStore.put(FieldName.WORKSPACE_ID, (workspace.getId()));
contextStore.put(FieldName.ARTIFACT_ID, (artifact.getId()));
}
@Override
public void afterEach(ExtensionContext extensionContext) {
ExtensionContext.Store contextStore = extensionContext.getStore(ExtensionContext.Namespace.create(ArtifactBuilderExtension.class));
String workspaceId = contextStore.get(FieldName.WORKSPACE_ID, String.class);
// Because right now we only have checks for apps
// Move this to artifact based model when we fix that
applicationService
.findByWorkspaceId(workspaceId, applicationPermission.getDeletePermission())
.flatMap(remainingApplication -> applicationPageService.deleteApplication(remainingApplication.getId()))
.collectList()
.block();
workspaceService.archiveById(workspaceId).block();
}
private Mono<? extends ArtifactExchangeJson> createArtifactJson(String filePath, Class<? extends ArtifactExchangeJson> exchangeJsonType) throws IOException {
ClassPathResource classPathResource = new ClassPathResource(filePath);
String artifactJson = classPathResource.getContentAsString(Charset.defaultCharset());
ArtifactExchangeJson artifactExchangeJson =
objectMapper.copy().disable(MapperFeature.USE_ANNOTATIONS).readValue(artifactJson, exchangeJsonType);
return jsonSchemaMigration.migrateArtifactExchangeJsonToLatestSchema(artifactExchangeJson, null, null);
}
}

View File

@ -1,52 +0,0 @@
package com.appsmith.server.git;
import com.appsmith.external.constants.PluginConstants;
import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.ActionDTO;
import com.appsmith.external.models.Datasource;
import com.appsmith.external.models.PluginType;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Artifact;
import com.appsmith.server.domains.Plugin;
import com.appsmith.server.plugins.base.PluginService;
import com.appsmith.server.services.LayoutActionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import java.util.UUID;
@Component
public class GitArtifactTestUtils<T extends Artifact> {
@Autowired
LayoutActionService layoutActionService;
@Autowired
PluginService pluginService;
Mono<Void> createADiff(Artifact artifact) {
Application application = (Application) artifact;
String pageId = application.getPages().get(0).getId();
Plugin plugin = pluginService.findByPackageName("restapi-plugin").block();
Datasource datasource = new Datasource();
datasource.setName(PluginConstants.DEFAULT_REST_DATASOURCE);
datasource.setWorkspaceId(application.getWorkspaceId());
datasource.setPluginId(plugin.getId());
ActionDTO action = new ActionDTO();
action.setPluginType(PluginType.API);
action.setName("aGetAction_" + UUID.randomUUID());
action.setDatasource(datasource);
action.setActionConfiguration(new ActionConfiguration());
action.getActionConfiguration().setHttpMethod(HttpMethod.GET);
action.setPageId(pageId);
return layoutActionService
.createSingleAction(action, Boolean.FALSE)
.then();
}
}

View File

@ -1,119 +0,0 @@
package com.appsmith.server.git;
import com.appsmith.git.configurations.GitServiceConfig;
import com.appsmith.server.applications.base.ApplicationService;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.GitAuth;
import com.appsmith.server.dtos.ArtifactExchangeJson;
import com.appsmith.server.git.common.CommonGitService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.reactive.function.client.WebClient;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import reactor.core.publisher.Mono;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import static org.assertj.core.api.Assertions.assertThat;
/**
* This extension is meant to set up the SSH keys for an artifact and link it to the git server.
* We'll also set up the repository based on the context,
* and ensure that all local FS directories for git are clean by the end of a suite
*/
@Component
public class GitServerInitializerExtension implements BeforeAllCallback, BeforeEachCallback, AfterEachCallback, AfterAllCallback {
@Autowired
ApplicationService applicationService;
@Autowired
GitServiceConfig gitServiceConfig;
private static GenericContainer<?> gitContainer = new GenericContainer<>(
CompletableFuture.completedFuture("appsmith/test-event-driver"))
.withExposedPorts(4200, 22)
.waitingFor(Wait.forHttp("/").forPort(4200).forStatusCode(200));
@Override
public void beforeAll(ExtensionContext extensionContext) {
gitContainer.start();
assertThat(gitContainer.isRunning()).isTrue();
}
@Override
public void beforeEach(ExtensionContext extensionContext) {
ExtensionContext.Store parentContextStore = extensionContext.getParent().get().getStore(ExtensionContext.Namespace.create(ArtifactBuilderExtension.class));
Class<? extends ArtifactExchangeJson> aClass = parentContextStore.get(ArtifactExchangeJson.class, Class.class);
String filePath = parentContextStore.get("filePath", String.class);
ExtensionContext.Store contextStore = extensionContext.getStore(ExtensionContext.Namespace.create(ArtifactBuilderExtension.class));
String artifactId = contextStore.get(FieldName.ARTIFACT_ID, String.class);
String repoName = "test" + artifactId;
// TODO : Move this to artifact service to enable packages
// Generate RSA public key for the given artifact
Mono<GitAuth> gitAuthMono = applicationService.createOrUpdateSshKeyPair(artifactId, "RSA");
String tedGitApiPath = "http://" + gitContainer.getHost() + ":" + gitContainer.getMappedPort(4200) + "/api/v1/git/";
// Attach public key on TED git server
Mono<ResponseEntity<Void>> createRepoMono = WebClient.create(tedGitApiPath + "repos")
.post()
.bodyValue(Map.of("name", repoName, "private", false))
.retrieve()
.toBodilessEntity();
Mono.zip(gitAuthMono, createRepoMono)
.flatMap(tuple2 -> {
GitAuth auth = tuple2.getT1();
String generatedKey = auth.getPublicKey();
return WebClient.create(tedGitApiPath + "/keys/" + repoName)
.post()
.bodyValue(Map.of("title", "key_" + UUID.randomUUID(),
"key", generatedKey,
"read_only", false))
.retrieve()
.toBodilessEntity();
})
.block();
}
@Override
public void afterEach(ExtensionContext extensionContext) {
// Delete all repositories created in the current workspace
ExtensionContext.Store contextStore = extensionContext.getStore(ExtensionContext.Namespace.create(ArtifactBuilderExtension.class));
String workspaceId = contextStore.get(FieldName.WORKSPACE_ID, String.class);
Path path = Paths.get(gitServiceConfig.getGitRootPath()).resolve(workspaceId);
FileSystemUtils.deleteRecursively(path.toFile());
}
@Override
public void afterAll(ExtensionContext extensionContext) {
// Stop the TED container
gitContainer.stop();
assertThat(gitContainer.isRunning()).isFalse();
}
public String getGitSshUrl(String repoName) {
return "ssh://git@" + gitContainer.getHost() +":" + gitContainer.getMappedPort(22) +"/git-server/repos/Cypress/" + repoName + ".git";
}
}

View File

@ -1,27 +0,0 @@
package com.appsmith.server.git;
import com.appsmith.server.constants.ArtifactType;
import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Artifact;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@RequiredArgsConstructor
@Component
public class GitTestUtils {
private final GitArtifactTestUtils<Application> gitApplicationTestUtils;
private GitArtifactTestUtils<?> getArtifactSpecificUtils(ArtifactType artifactType) {
// TODO For now just work with apps
return gitApplicationTestUtils;
}
public Mono<Void> createADiffInArtifact(Artifact artifact) {
GitArtifactTestUtils<?> artifactSpecificUtils = getArtifactSpecificUtils(artifact.getArtifactType());
return artifactSpecificUtils.createADiff(artifact);
}
}