diff --git a/.github/workflows/e2e-testing.yaml b/.github/workflows/e2e-testing.yaml new file mode 100644 index 000000000..70492675e --- /dev/null +++ b/.github/workflows/e2e-testing.yaml @@ -0,0 +1,33 @@ +# 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. + +name: E2E Testing +on: + - pull_request + +jobs: + e2e-test: + name: Run E2E Test + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Test + run: | + sudo curl -L https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose + sudo chmod u+x /usr/local/bin/docker-compose + make test-e2e diff --git a/Makefile b/Makefile index 921076c82..354819854 100644 --- a/Makefile +++ b/Makefile @@ -28,8 +28,13 @@ generate: @$(GO) generate ./... @$(GO) mod tidy +image: + @$(DOCKER_CMD) build . -t answerdev/answer:latest + test: @$(GO) test ./internal/repo/repo_test +test-e2e: image + cd e2e && ./start.sh # clean all build result clean: diff --git a/e2e/Dockerfile b/e2e/Dockerfile new file mode 100644 index 000000000..bfab2b7aa --- /dev/null +++ b/e2e/Dockerfile @@ -0,0 +1,23 @@ +# 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. + +FROM docker.io/linuxsuren/api-testing:v0.0.14 + +WORKDIR /workspace +COPY testsuite.yaml . + +CMD [ "atest", "run", "-p", "testsuite.yaml"] diff --git a/e2e/compose.yaml b/e2e/compose.yaml new file mode 100644 index 000000000..54c0ba3cf --- /dev/null +++ b/e2e/compose.yaml @@ -0,0 +1,84 @@ +# 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. + +version: "3.9" + +services: + testing: + build: . + depends_on: + answer: + condition: service_healthy + + answer: + image: "answerdev/answer:latest" # this is should be a local image, do not need to change it + environment: + - AUTO_INSTALL=true + - DB_TYPE=mysql + - DB_USERNAME=root + - DB_PASSWORD=password + - DB_HOST=mysql:3306 + - DB_NAME=answer + - LANGUAGE=en_US + - SITE_NAME=answer-test + - SITE_URL=http://localhost + - CONTACT_EMAIL=test@answer.com + - ADMIN_NAME=admin123 + - ADMIN_PASSWORD=admin123 + - ADMIN_EMAIL=admin123@admin.com + volumes: + - answer-data:/data + healthcheck: + test: ["CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/80"] + interval: 3s + timeout: 30s + retries: 10 + start_period: 3s + ports: + - 8080:80 + depends_on: + mysql: + condition: service_healthy + links: + - mysql + deploy: + resources: + limits: + memory: 4000M + + mysql: + image: mysql:8.2.0 + environment: + - MYSQL_DATABASE=answer + - MYSQL_ROOT_PASSWORD=password + - MYSQL_USER=mysql + - MYSQL_PASSWORD=mysql + healthcheck: + test: [ "CMD", "mysqladmin" ,"ping", "-uroot", "-ppassword" ] + timeout: 20s + retries: 10 + command: ['mysqld', '--character-set-server=utf8mb4', '--collation-server=utf8mb4_unicode_ci', '--skip-character-set-client-handshake'] + volumes: + - sql_data:/var/lib/mysql + deploy: + resources: + limits: + memory: 500M + +volumes: + answer-data: + sql_data: diff --git a/e2e/start.sh b/e2e/start.sh new file mode 100755 index 000000000..1111c57c2 --- /dev/null +++ b/e2e/start.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# 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. + +file=$1 +if [ "$file" == "" ] +then + file=compose.yaml +fi + +docker-compose version +docker-compose -f "$file" up --build -d + +while true +do + docker-compose -f "$file" ps | grep testing + if [ $? -eq 1 ] + then + code=-1 + docker-compose -f "$file" logs | grep e2e-testing + docker-compose -f "$file" logs | grep e2e-testing | grep Usage + if [ $? -eq 1 ] + then + code=0 + echo "successed" + fi + + docker-compose -f "$file" down + set -e + exit $code + fi + sleep 1 +done diff --git a/e2e/testsuite.yaml b/e2e/testsuite.yaml new file mode 100644 index 000000000..e5ca20401 --- /dev/null +++ b/e2e/testsuite.yaml @@ -0,0 +1,181 @@ +# 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. + +name: answer +api: | + {{default "http://answer" (env "SERVER")}}/answer/api/v1 +param: + email: | + {{default "admin123@admin.com" (env "EMAIL")}} + pass: | + {{default "admin123" (env "PASSWD")}} +items: +- name: login + request: + api: /user/login/email + body: |- + { + "e_mail": "{{.param.email}}", + "pass": "{{.param.pass}}" + } + header: + Content-Type: application/json + method: POST +- name: status + request: + api: /notification/status + header: + Authorization: "{{.login.data.access_token}}" + Content-Type: application/json + method: GET +- name: question + request: + api: /question + body: | + { + "title": "{{randomKubernetesName}}", + "content": "good-body", + "tags": [ + { + "slug_name": "test", + "display_name": "test", + "original_text": "", + "parsed_text": "" + } + ] + } + header: + Authorization: '{{.login.data.access_token}}' + Content-Type: application/json + method: POST +- name: questionList + request: + api: /question/page + header: + Authorization: '{{.login.data.access_token}}' +- name: answer + request: + api: /answer + body: | + { + "question_id": "{{(index .questionList.data.list 0).id}}", + "content": "{{randomKubernetesName}}", + "html": "

{{randomKubernetesName}}

\n" + } + header: + Authorization: '{{.login.data.access_token}}' + Content-Type: application/json + method: POST +- before: + items: + - sleep("1s") + expect: {} + name: acceptance + request: + api: /answer/acceptance + body: | + { + "question_id": "{{.question.data.id}}", + "answer_id": "{{.answer.data.info.id}}" + } + header: + Authorization: '{{.login.data.access_token}}' + Content-Type: application/json + method: POST +- expect: {} + name: delAnswer + request: + api: /answer + body: | + { + "id": "{{.answer.data.info.id}}" + } + header: + Authorization: '{{.login.data.access_token}}' + Content-Type: application/json + method: DELETE +- expect: {} + name: delQuestion + request: + api: /question + body: | + { + "id": "{{.question.data.id}}" + } + header: + Authorization: '{{.login.data.access_token}}' + Content-Type: application/json + method: DELETE +- expect: + bodyFieldsExpect: + msg: page must be 1 or greater + statusCode: 400 + name: search-nagetive-page + request: + api: /search?q=abc&order=active&page={{randInt -10 0}}&size=20 + header: + Authorization: '{{.login.data.access_token}}' + Content-Type: application/json +- expect: + bodyFieldsExpect: + msg: size must be 1 or greater + statusCode: 400 + name: search-nagetive-size + request: + api: /search?q=abc&order=active&page=1&size={{randInt -10 0}} + header: + Authorization: '{{.login.data.access_token}}' + Content-Type: application/json +- expect: + bodyFieldsExpect: + msg: order must be one of [newest active score relevance] + statusCode: 400 + name: search-wrong-order + request: + api: /search?q=abc&order={{randAlpha 6}}&page=1&size=10 + header: + Authorization: '{{.login.data.access_token}}' + Content-Type: application/json +- name: search-not-found + request: + api: /search?q={{randAlpha 16}}&order={{index (list "newest" "active" "score" "relevance") (randInt 0 3)}}&page=1&size=10 + header: + Authorization: '{{.login.data.access_token}}' + Content-Type: application/json +- name: tags + request: + api: /tags/page?page=1&page_size=20&query_cond={{index (list "newest" "popular" "name") (randInt 0 2)}} + header: + Authorization: '{{.login.data.access_token}}' + Content-Type: application/json +- expect: + bodyFieldsExpect: + msg: query_cond must be one of [popular name newest] + statusCode: 400 + name: tags-invalid-cond + request: + api: /tags/page?page=1&page_size=20&query_cond={{randAlpha 16}} + header: + Authorization: '{{.login.data.access_token}}' + Content-Type: application/json +- expect: {} + name: user-ranking + request: + api: /user/ranking + header: + Authorization: '{{.login.data.access_token}}' + Content-Type: application/json