Git integration
Since of 2.10 Figaf Tool supports integration with Git for CPI and API Management Agent systems. It includes:
-
Synchronization of all IFlows(for CPI)/API Proxies(for API Management) with configured Git repository. All found changes will be saved in the repository during CTT synchronization. It’s also possible to initialize/reinitialize repository by pushing all objects through manual operation. See this section to learn more about configuration options.
-
IRT Gradle plugin for running Figaf Tool test suites remotely through Gradle task. It helps to build flexible development workflow with unit and integration tests.
It’s needed to run the command git config --system core.longpaths true as an admin in command prompt if you use OS Windows in order to prevent a problem with long file names.
1. Git integration configuration
Open Configuration → Agents page, click Edit on needed Agent. If you don’t have Agent configured read this section at first. Enable checkbox Enable Git Integration. Then you will see the following fields:
-
Skip Synchronization Of IFlows In Git(CPI) orSkip Synchronization Of API Proxies In Git(Api Management) - if true, IFlows or API Proxies won’t be added to git repository (only folders will be initialized). -
Update build.gradle automatically- if true,build.gradlewill be updated automatically after synchronization. Enable this option if you don’t need to changebuild.gradlemanually. If this option is disabled, you have to updatebuild.gradlefile when version of gradle plugin is upgraded. -
Update settings.gradle automatically- if true,settings.gradlewill be updated automatically after synchronization. Enable this option if you don’t need to changesettings.gradlemanually. If this option is disabled, you have to updatesettings.gradlefile when objects, stored in Git, are changed on remote system. -
Update .gitignore automatically- if true,.gitignorewill be updated automatically after synchronization. Enable this option if you don’t need to change.gitignoremanually. -
Add scenario documentation(only for CPI) - if true, scenario documentation (without the PNG) is generated and added to local folder (documentation isn’t added to repository). The option is enabled only ifSkip Synchronization Of IFlows In Gitisn’t enabled. -
Integrate with Git Pipelines(only for CPI) - if true, then Figaf Tool enables integration with git pipeline to validate updated IFlows. The option is enabled only ifSkip Synchronization Of IFlows In Gitisn’t enabled. See Integration with Git Pipelines for more details. -
Git Remote Url- url to remote repository. -
Local Path To Repo(only for on-premise version) - local folder, where repository will be cloned. If it’s not defined, it will use/<origin repository name>as a local path relatively toirt.jar. You can define either absolute or relative paths here, a relative path will be calculated from the same folder where Figaf Tool is executed. -
Branch Name- branch used by Figaf Tool. If it doesn’t exist, it will be created automatically. We recommend to define a special branch for Figaf Tool and then merge it with your working branch when it needs. Changes are committed to this branch during synchronization operation. But you can also initialize/reinitialize a full state of all objects at one time manually. -
Git Username- username for accessing remote Git repository. Figaf Tool will make commits on behalf specified user, but with its own name in commits. -
Git Password- password related to specified Git user. -
Ignore Files- paths used when you are downloading/uploading IFlow/ApiProxy through cpi-plugin or api-management-plugin. All matched files and folders won’t be added to bundled archive. Define only relative paths, the root is IFlow/ApiProxy bundled folder. By default it ignoressrc/test,build.gradle,settings.gradle,gradle.properties. The main reason to have some project files excluded is that they are not a part of bundled model. But some of them (likesrc/test, where you can keep your unit tests for groovy scripts) makes CPI IFlow non-operational, i.e. upload will be successful, but IFlow won’t work in runtime.
|
The following packages on CPI won’t be added to the repository during synchronization:
|
|
If you want to add lib files of some iflow to the index, put the following line into
|
2. Integration with Git Pipelines
Git Pipelines work only for repositories in GitHub.
The following configuration should be done to get the integration working:
-
Set up a workflow yourself in Git repository:
-
Configure
figaf-validation.ymlfile:figaf-validation.yml
name: Figaf Pipeline on: push: branches: - '**' jobs: figaf-validations: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v2 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - name: Set up Java 11 uses: actions/setup-java@v2 with: distribution: 'adopt' java-version: '11' - name: Download and extract CPI Lint run: | curl -L -o cpilint.zip https://github.com/mwittrock/cpilint/releases/download/v1.0.4/cpilint-1.0.4.zip unzip -o cpilint.zip rm cpilint.zip chmod +x cpilint-1.0.4/bin/cpilint - name: Setup Gradle uses: gradle/gradle-build-action@v2 with: gradle-version: 7.4 - name: Get changed IFlow directories run: | changedIflowDirs=$(git diff --name-only --diff-filter=d HEAD^ | grep -E '.*/IntegrationFlow/[^/]+' | awk -F/ '{print $1"/"$2"/"$3}' | sort | uniq) echo "Changed IFlow Directories: $changedIflowDirs" EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) echo "changedIflowDirs<<$EOF" >> ${GITHUB_ENV} echo "${changedIflowDirs}" >> ${GITHUB_ENV} echo "$EOF" >> ${GITHUB_ENV} - name: CPI Lint validation run: | if [ -z "${{ env.changedIflowDirs }}" ]; then echo "No IFlow changes detected, skipping CPI Lint validation" exit 0 fi readarray -t changedIflowDirs <<< "${{ env.changedIflowDirs }}" for integrationFlowDir in "${changedIflowDirs[@]}" do echo "integrationFlowDir: $integrationFlowDir" iflowName="$(basename "$integrationFlowDir")" packageName="$(basename "$(dirname "$(dirname "$integrationFlowDir")")")" (cd "$integrationFlowDir" && zip -r "../$iflowName.zip" .) cpilintCommand="${{ github.workspace }}/cpilint-1.0.4/bin/cpilint -rules=${{ github.workspace }}/.github/workflows/cpilint.xml -files=\"../$iflowName.zip\"" echo "Executing cpilint: $cpilintCommand" cpilintStatus=0 output=$(cd "$integrationFlowDir" && eval $cpilintCommand 2>&1) || cpilintStatus=$? if [ -z "$cpilintStatus" ]; then cpilintStatus=0 fi echo "CPI Lint output: $output" echo "CPI Lint exit code: $cpilintStatus" echo "Validation name: CPI Lint" > "$integrationFlowDir/pipeline-validation.txt" echo "Execution time: $(date +'%Y-%m-%d %H:%M:%S')" >> "$integrationFlowDir/pipeline-validation.txt" echo "Exit code: $cpilintStatus" >> "$integrationFlowDir/pipeline-validation.txt" echo -e "Output: $output\n" >> "$integrationFlowDir/pipeline-validation.txt" rm -f "$integrationFlowDir.zip" done - name: Run unit tests run: | if [ -z "${{ env.changedIflowDirs }}" ]; then echo "No IFlow changes detected, skipping Unit tests validation" exit 0 fi readarray -t changedIflowDirs <<< "${{ env.changedIflowDirs }}" for integrationFlowDir in "${changedIflowDirs[@]}" do iflowName="$(basename "$integrationFlowDir")" packageName="$(basename "$(dirname "$(dirname "$integrationFlowDir")")")" runTestsCommand="gradle :$packageName:iflow-$iflowName:test" echo "Executing runTestsCommand: $runTestsCommand" runTestsStatus=0 output=$(eval $runTestsCommand 2>&1) || runTestsStatus=$? if [ -z "$runTestsStatus" ]; then runTestsStatus=0 fi echo "Unit tests output: $output" echo "Unit tests exit code: $runTestsStatus" echo "Validation name: Unit tests" >> "$integrationFlowDir/pipeline-validation.txt" echo "Execution time: $(date +'%Y-%m-%d %H:%M:%S')" >> "$integrationFlowDir/pipeline-validation.txt" echo "Exit code: $runTestsStatus" >> "$integrationFlowDir/pipeline-validation.txt" echo -e "Output: $output\n" >> "$integrationFlowDir/pipeline-validation.txt" done - name: Commit and push changes run: | if [[ $(git status --porcelain) ]]; then git config --local user.email "[email protected]" git config --local user.name "Figaf" git add . git commit -m "Update pipeline-validation.txt" git push else echo "No changes to commit" fi -
Configure
cpilint.xml:cpilint.xml
<?xml version="1.0"?> <cpilint> <rules> <iflow-description-required/> <disallowed-scripting-languages> <disallow>javascript</disallow> </disallowed-scripting-languages> </rules> </cpilint>
Push the files to remote repository. -
-
Go to agent for which you want to configure integration with Git Pipeline and enable
Integrate with Git Pipelines. -
Init/Reinit Git repository for the agent.
Now each updated IFlow will be validated automatically and the result will be saved into pipeline-validation.txt file in the IFlow folder in the repository.
Then you can check Git Pipeline Validation Status on Tracked object page and this validation will be integrated into validation of transports.
Gradle version used locally and in Git pipeline should be the same. In our example version 7.4 is used.
|
3. Groovy scripts unit testing
Figaf Tool provides a possibility to generate unit tests for groovy scripts using the state of the recorded messages in CPI test cases.
To use that feature you should open a Messages tab on the Test Case page. It has a button Generate groovy scripts test data.
There are two options:
-
Add to git repository. If git integration is configured and enabled, Figaf Tool will build test data files and commit them to repository.
-
Download archive. All needed files will be archived and downloaded to your computer.
There are several files which are generated for testing:
-
Files in
commonfolder. These files are the basis for unit testing of groovy scripts.
GroovyTestDataandMessageTestDataare a model.
MessageImplis a simple implementation of SAP Message interface.
AbstractGroovyTestis an abstract class which does all the testing and has several methods which can be extended. -
Files in
<iflow_package_name>/<iflow_name>folder.
GroovyScriptsTest.groovyis a test which is ready to be run. It has all the needed links and methods.
Files inresourcesfolder are the test data forGroovyScriptsTest.groovy. Each file has input and expected output data. Each file contains an integer in the end of the name. If you selectDownload archivethese numbers just start from 1. If you selectAdd to git repositoryFigaf Tool will not overwrite the existing test files. Instead of it Figaf Tool will find the maximum number among the existing files and increment this number.
|
You need to manually download CPI client jars and put them to the
|
You can extend testing and write custom tests using AbstractGroovyTest.groovy. It’s not recommended to edit GroovyScriptsTest.groovy because it can be overwritten next time by Figaf Tool. It’s better to write custom tests in a separate class which should extend AbstractGroovyTest. The following methods can be overridden:
-
processMessageData(String groovyScriptPath, String testDataFilePath, String methodName)- it callsmethodNameof the groovy script for test data and returnsmessageDataExpectedandmessageDataActual. -
assertMessages(MessageTestData messageDataExpected, MessageTestData messageDataActual, List<String> ignoredKeysPrefixes, List<String> ignoredKeys)- assertmessageDataExpectedandmessageDataActualexcluding the keys presented inignoredKeysPrefixesandignoredKeys. -
basicGroovyScriptTest(String groovyScriptPath, String testDataFilePath, String methodName, List<String> ignoredKeysPrefixes, List<String> ignoredKeys)- aggregate two previous methods. -
getIgnoredKeysPrefixes()- if a key starts from this list’s value, this key will be excluded from assertion inassertMessagesmethod. -
getIgnoredKeys()- if a key is presented in this list, this key will be excluded from assertion inassertMessagesmethod.
For example if you want to use processMessageData but have your own assertions you can easily do it.
import org.junit.jupiter.api.Test
import static org.assertj.core.api.Assertions.assertThat
import static org.mockito.BDDMockito.then
class SampleTest extends AbstractGroovyTest {
@Test
void customTest() {
// given
initMessageLogFactoryMocks()
String groovyScriptPath = "src/main/resources/script/script1.groovy"
String testDataFilePath = "src/test/resources/test-data-files/script1/processData/test-data-1.json"
// when
def (MessageTestData messageDataExpected, MessageTestData messageDataActual) = processMessageData(groovyScriptPath, testDataFilePath, "processData")
// then
// assert custom messageLog interaction
then(messageLog).should().addAttachmentAsString("error.xml", messageDataExpected.getBody() as String, "application/xml")
then(messageLog).should().addAttachmentAsString("responseError", "daniel test attachment", "text/plain")
// assert properties/headers/body
def propertyKey = "newError3"
String actualPropertyValue = messageDataActual.getProperties().get(propertyKey)
assertThat(actualPropertyValue).
overridingErrorMessage("Property with key '%s' must be not null", propertyKey).
isNotNull()
assertThat(actualPropertyValue).
overridingErrorMessage("Property with key '%s' must end with 'Test3', but actual value was '%s'", propertyKey, actualPropertyValue).
endsWith("Test3")
}
}