test: Git integration tests (#38237)
This commit is contained in:
parent
c5238ffd03
commit
e45cbdfa83
|
|
@ -13,6 +13,8 @@ public interface ArtifactCE {
|
|||
|
||||
String getName();
|
||||
|
||||
void setName(String artifactName);
|
||||
|
||||
String getWorkspaceId();
|
||||
|
||||
Boolean getExportWithConfiguration();
|
||||
|
|
|
|||
|
|
@ -2187,13 +2187,17 @@ public class CommonGitServiceCEImpl implements CommonGitServiceCE {
|
|||
.onErrorResume(throwable -> {
|
||||
log.error("Delete branch failed {}", throwable.getMessage());
|
||||
if (throwable instanceof CannotDeleteCurrentBranchException) {
|
||||
return Mono.error(new AppsmithException(
|
||||
AppsmithError.GIT_ACTION_FAILED,
|
||||
"delete branch",
|
||||
"Cannot delete current checked out branch"));
|
||||
return releaseFileLock(baseArtifactId)
|
||||
.then(Mono.error(new AppsmithException(
|
||||
AppsmithError.GIT_ACTION_FAILED,
|
||||
"delete branch",
|
||||
"Cannot delete current checked out branch")));
|
||||
}
|
||||
return Mono.error(new AppsmithException(
|
||||
AppsmithError.GIT_ACTION_FAILED, "delete branch", throwable.getMessage()));
|
||||
return releaseFileLock(baseArtifactId)
|
||||
.then(Mono.error(new AppsmithException(
|
||||
AppsmithError.GIT_ACTION_FAILED,
|
||||
"delete branch",
|
||||
throwable.getMessage())));
|
||||
})
|
||||
.flatMap(isBranchDeleted ->
|
||||
releaseFileLock(baseArtifactId).map(status -> isBranchDeleted))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,584 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
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 {}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
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);
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
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";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user