Skip to content

Commit

Permalink
[Feature-10411] Add tenant api test (#10442)
Browse files Browse the repository at this point in the history
* add feature_10411

* add feature_10411

* update README.md

* fix README.md deadlink

* fix error log output

* fix comment
  • Loading branch information
SbloodyS authored Jun 15, 2022
1 parent d516369 commit e507394
Show file tree
Hide file tree
Showing 30 changed files with 2,220 additions and 0 deletions.
139 changes: 139 additions & 0 deletions .github/workflows/api-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

on:
pull_request:
push:
branches:
- dev

name: API-Test

concurrency:
group: api-test-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true


jobs:
paths-filter:
name: API-Test-Path-Filter
runs-on: ubuntu-latest
outputs:
not-ignore: ${{ steps.filter.outputs.not-ignore }}
steps:
- uses: actions/checkout@v2
- uses: dorny/paths-filter@b2feaf19c27470162a626bd6fa8438ae5b263721
id: filter
with:
filters: |
not-ignore:
- '!(docs/**)'
build:
name: API-Test-Build
needs: paths-filter
if: ${{ (needs.paths-filter.outputs.not-ignore == 'true') || (github.event_name == 'push') }}
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Sanity Check
uses: ./.github/actions/sanity-check
- name: Cache local Maven repository
uses: actions/cache@v3
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-maven-
- name: Build Image
run: |
./mvnw -B clean install \
-Dmaven.test.skip \
-Dmaven.javadoc.skip \
-Dmaven.checkstyle.skip \
-Pdocker,release -Ddocker.tag=ci \
-pl dolphinscheduler-standalone-server -am
- name: Export Docker Images
run: |
docker save apache/dolphinscheduler-standalone-server:ci -o /tmp/standalone-image.tar \
&& du -sh /tmp/standalone-image.tar
- uses: actions/upload-artifact@v2
name: Upload Docker Images
with:
name: standalone-image
path: /tmp/standalone-image.tar
retention-days: 1
api-test:
name: ${{ matrix.case.name }}
needs: build
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
case:
- name: Tenant
class: org.apache.dolphinscheduler.api.test.cases.TenantAPITest
env:
RECORDING_PATH: /tmp/recording-${{ matrix.case.name }}
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Cache local Maven repository
uses: actions/cache@v3
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-maven-
- uses: actions/download-artifact@v2
name: Download Docker Images
with:
name: standalone-image
path: /tmp
- name: Load Docker Images
run: |
docker load -i /tmp/standalone-image.tar
- name: Run Test
run: |
./mvnw -B -f dolphinscheduler-api-test/pom.xml -am \
-DfailIfNoTests=false \
-Dtest=${{ matrix.case.class }} test
- uses: actions/upload-artifact@v2
if: always()
name: Upload Recording
with:
name: recording-${{ matrix.case.name }}
path: ${{ env.RECORDING_PATH }}
retention-days: 1
result:
name: API-Test-Result
runs-on: ubuntu-latest
timeout-minutes: 30
needs: [ api-test, paths-filter ]
if: always()
steps:
- name: Status
run: |
if [[ ${{ needs.paths-filter.outputs.not-ignore }} == 'false' && ${{ github.event_name }} == 'pull_request' ]]; then
echo "Skip API Test!"
exit 0
fi
if [[ ${{ needs.api-test.result }} != 'success' ]]; then
echo "API test Failed!"
exit -1
fi
44 changes: 44 additions & 0 deletions dolphinscheduler-api-test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# DolphinScheduler Backend API Test

## Page Object Model

DolphinScheduler API test respects
the [Page Object Model (POM)](https://www.selenium.dev/documentation/guidelines/page_object_models/) design pattern.
Every page of DolphinScheduler's api is abstracted into a class for better maintainability.

### Example

The login page's api is abstracted
as [`LoginPage`](dolphinscheduler-api-test-case/src/test/java/org/apache/dolphinscheduler/api.test/pages/LoginPage.java), with the
following fields,

```java
public HttpResponse login(String username, String password) {
Map<String, Object> params = new HashMap<>();

params.put("userName", username);
params.put("userPassword", password);

RequestClient requestClient = new RequestClient();

return requestClient.post("/login", null, params);
}
```

where `userName`, `userPassword` are the main elements on UI that we are interested in.

## Test Environment Setup

DolphinScheduler API test uses [testcontainers](https://www.testcontainers.org) to set up the testing
environment, with docker compose.

Typically, every test case needs one or more `docker-compose.yaml` files to set up all needed components, and expose the
DolphinScheduler UI port for testing. You can use `@DolphinScheduler(composeFiles = "")` and pass
the `docker-compose.yaml` files to automatically set up the environment in the test class.

```java

@DolphinScheduler(composeFiles = "docker/tenant/docker-compose.yaml")
class TenantAPITest {
}
```
40 changes: 40 additions & 0 deletions dolphinscheduler-api-test/dolphinscheduler-api-test-case/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Licensed to Apache Software Foundation (ASF) under one or more contributor
~ license agreements. See the NOTICE file distributed with
~ this work for additional information regarding copyright
~ ownership. Apache Software Foundation (ASF) licenses this file to you under
~ the Apache License, Version 2.0 (the "License"); you may
~ not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing,
~ software distributed under the License is distributed on an
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
~ KIND, either express or implied. See the License for the
~ specific language governing permissions and limitations
~ under the License.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>dolphinscheduler-api-test</artifactId>
<groupId>org.apache.dolphinscheduler</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>dolphinscheduler-api-test-case</artifactId>

<dependencies>
<dependency>
<groupId>org.apache.dolphinscheduler</groupId>
<artifactId>dolphinscheduler-api-test-core</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Licensed to Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Apache Software Foundation (ASF) licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.dolphinscheduler.api.test.cases;

import lombok.extern.slf4j.Slf4j;
import org.apache.dolphinscheduler.api.test.core.DolphinScheduler;
import org.apache.dolphinscheduler.api.test.entity.HttpResponse;
import org.apache.dolphinscheduler.api.test.entity.LoginResponseData;
import org.apache.dolphinscheduler.api.test.entity.TenantListPagingResponseData;
import org.apache.dolphinscheduler.api.test.entity.TenantListPagingResponseTotalList;
import org.apache.dolphinscheduler.api.test.pages.LoginPage;
import org.apache.dolphinscheduler.api.test.pages.security.TenantPage;
import org.apache.dolphinscheduler.api.test.utils.JSONUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;


@DolphinScheduler(composeFiles = "docker/basic/docker-compose.yaml")
@Slf4j
public class TenantAPITest {
private static final String tenant = System.getProperty("user.name");

private static final String user = "admin";

private static final String password = "dolphinscheduler123";

private static String sessionId = null;

private static Integer existTenantId = null;

@BeforeAll
public static void setup() {
LoginPage loginPage = new LoginPage();
HttpResponse loginHttpResponse = loginPage.login(user, password);

sessionId = JSONUtils.convertValue(loginHttpResponse.body().data(), LoginResponseData.class).sessionId();
}

@AfterAll
public static void cleanup() {
LOGGER.info("success cleanup");
}

@Test
@Order(1)
public void testCreateTenant() {
TenantPage tenantPage = new TenantPage();

HttpResponse createTenantHttpResponse = tenantPage.createTenant(sessionId, tenant, 1, "");

Assertions.assertTrue(createTenantHttpResponse.body().success());
}

@Test
@Order(2)
public void testDuplicateCreateTenant() {
TenantPage tenantPage = new TenantPage();

HttpResponse createTenantHttpResponse = tenantPage.createTenant(sessionId, tenant, 1, "");

Assertions.assertFalse(createTenantHttpResponse.body().success());
}

@Test
@Order(5)
public void testGetTenantListPaging() {
TenantPage tenantPage = new TenantPage();

HttpResponse createTenantHttpResponse = tenantPage.getTenantListPaging(sessionId, 1, 10, "");
boolean result = false;

for (TenantListPagingResponseTotalList tenantListPagingResponseTotalList : JSONUtils.convertValue(createTenantHttpResponse.body().data(), TenantListPagingResponseData.class).totalList()) {
if (tenantListPagingResponseTotalList.tenantCode().equals(tenant)) {
result = true;
existTenantId = tenantListPagingResponseTotalList.id();
break;
}
}

Assertions.assertTrue(createTenantHttpResponse.body().success());
Assertions.assertTrue(result);
}

@Test
@Order(10)
public void testDeleteTenant() {
TenantPage tenantPage = new TenantPage();

HttpResponse deleteTenantHttpResponse = tenantPage.deleteTenant(sessionId, existTenantId);

Assertions.assertTrue(deleteTenantHttpResponse.body().success());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Licensed to Apache Software Foundation (ASF) under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Apache Software Foundation (ASF) licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.dolphinscheduler.api.test.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class HttpResponse {

private int statusCode;

private HttpResponseBody body;
}
Loading

0 comments on commit e507394

Please sign in to comment.