diff --git a/.github/scripts/review-apps.sh b/.github/scripts/review-apps.sh
index 64fff197..fde890ba 100755
--- a/.github/scripts/review-apps.sh
+++ b/.github/scripts/review-apps.sh
@@ -2,7 +2,7 @@ set -e
echo "Review App Script"
# create "unique" name for fly review app
-app="mvp-pr-$PR_NUMBER"
+app=$APP_NAME
secrets="AUTH_API_KEY=$AUTH_API_KEY ENCRYPTION_KEYS=$ENCRYPTION_KEYS"
if [ "$EVENT_ACTION" = "closed" ]; then
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f8cdd33f..064187fd 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,8 +3,6 @@ name: Elixir CI
on:
push:
branches: [ main ]
- pull_request:
- branches: [ main ]
jobs:
@@ -56,64 +54,13 @@ jobs:
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
- # API Definition testing
- # https://docs.hoppscotch.io/cli
- api_definition:
- name: API Definition Tests
- runs-on: ubuntu-latest
- services:
- postgres:
- image: postgres:12
- ports: ['5432:5432']
- env:
- POSTGRES_PASSWORD: postgres
- options: >-
- --health-cmd pg_isready
- --health-interval 10s
- --health-timeout 5s
- --health-retries 5
- strategy:
- matrix:
- otp: ['25.1.2']
- elixir: ['1.14.2']
- steps:
- - uses: actions/checkout@v2
- - name: Set up Elixir
- uses: erlef/setup-beam@v1
- with:
- otp-version: ${{ matrix.otp }}
- elixir-version: ${{ matrix.elixir }}
- - name: Restore deps and _build cache
- uses: actions/cache@v3
- with:
- path: |
- deps
- _build
- key: deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }}
- restore-keys: |
- deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}
- - name: Install dependencies
- run: mix deps.get
-
- - name: Install Hoppscotch CLI
- run: npm i -g @hoppscotch/cli
-
- # Setups database and adds seed data for API definition tests
- - name: Run mix setup
- run: mix ecto.setup
- env:
- MIX_ENV: dev
- AUTH_API_KEY: ${{ secrets.AUTH_API_KEY }}
-
- - name: Running server and Hoppscotch Tests
- run: mix phx.server & sleep 5 && hopp test -e ./lib/api/localhost.json ./lib/api/MVP.json
# Continuous Deployment to Fly.io
# https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/
deploy:
name: Deploy app
runs-on: ubuntu-latest
- needs: [build, api_definition]
+ needs: [build]
# https://stackoverflow.com/questions/58139406/only-run-job-on-specific-branch-with-github-actions
if: github.ref == 'refs/heads/main'
env:
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
new file mode 100644
index 00000000..5b4ffa78
--- /dev/null
+++ b/.github/workflows/pr.yml
@@ -0,0 +1,127 @@
+name: PR CI
+
+on:
+ pull_request:
+ branches: [ main ]
+ types: [opened, reopened, synchronize, closed]
+
+jobs:
+
+ # BUILD AND UNIT TESTING
+ build:
+
+ # Only run when not closed
+ if: github.event.pull_request.action != 'closed'
+
+ name: Build and Test
+ runs-on: ubuntu-latest
+ services:
+ postgres:
+ image: postgres:12
+ ports: ['5432:5432']
+ env:
+ POSTGRES_PASSWORD: postgres
+ options: >-
+ --health-cmd pg_isready
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 5
+ strategy:
+ matrix:
+ otp: ['25.1.2']
+ elixir: ['1.14.2']
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up Elixir
+ uses: erlef/setup-beam@v1
+ with:
+ otp-version: ${{ matrix.otp }}
+ elixir-version: ${{ matrix.elixir }}
+ - name: Restore deps and _build cache
+ uses: actions/cache@v3
+ with:
+ path: |
+ deps
+ _build
+ key: deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}-${{ hashFiles('**/mix.lock') }}
+ restore-keys: |
+ deps-${{ runner.os }}-${{ matrix.otp }}-${{ matrix.elixir }}
+ - name: Install dependencies
+ run: mix deps.get
+ - name: Check code is formatted
+ run: mix format --check-formatted
+ - name: Run Tests
+ run: mix coveralls.json
+ env:
+ MIX_ENV: test
+ AUTH_API_KEY: ${{ secrets.AUTH_API_KEY }}
+ ENCRYPTION_KEYS: ${{ secrets.ENCRYPTION_KEYS }}
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v1
+
+
+
+ # DEPLOY THE REVIEW APP
+ # This will deploy an app to fly.io with the name 'mvp-pr-$PR_NUMBER' (check `review-apps.sh` script).
+ review_app:
+
+ # Only run when it's not a dependabot PR
+ if: github.event.pull_request.user.login != 'dependabot[bot]'
+
+ name: Review App Job
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Install flyctl
+ run: curl -L https://fly.io/install.sh | FLYCTL_INSTALL=/usr/local sh
+
+ - name: Set up Elixir
+ uses: erlef/setup-beam@v1
+ with:
+ otp-version: 24.3.4
+ elixir-version: 1.14.1
+
+ - name: Run Review App Script
+ run: ./.github/scripts/review-apps.sh
+ env:
+ ENCRYPTION_KEYS: ${{ secrets. ENCRYPTION_KEYS }}
+ AUTH_API_KEY: ${{ secrets.FLY_AUTH_API_KEY }}
+ APP_NAME: ${{ format('mvp-pr-{0}', github.event.number) }}
+ EVENT_ACTION: ${{ github.event.action }}
+ FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
+ FLY_ORG: dwyl-mvp
+ FLY_REGION: lhr
+ FLY_POSTGRES_NAME: mvp-db
+
+
+ # API DEFINITION TESTING - https://docs.hoppscotch.io/cli
+ # NOTE: The tests are executed sequentially in each folder
+ # but all the sequence of folders are run in reverse order of what's displayed in the GUI.
+ # We've made use of env variables to make it work. Take this into account if you want to add more requests.
+ api_definition:
+
+ # Only run when not closed
+ if: github.event.pull_request.action != 'closed'
+
+ name: API Definition Tests
+ runs-on: ubuntu-latest
+ needs: [review_app]
+ steps:
+ - uses: actions/checkout@v2
+
+ # After the app is deployed, we need to change the host
+ # in the `lib/api/fly_dev.json` so the API tests work and target the deployed app.
+ - name: Update lib/api/fly_dev.json description
+ uses: jossef/action-set-json-field@v2.1
+ with:
+ file: ./lib/api/fly_dev.json
+ field: host
+ value: ${{ format('https://mvp-pr-{0}.fly.dev', github.event.number) }}
+
+ - name: Install Hoppscotch CLI
+ run: npm i -g @hoppscotch/cli@0.3.0
+
+ - name: Running server and Hoppscotch Tests
+ run: hopp test -e ./lib/api/fly_dev.json ./lib/api/MVP.json
\ No newline at end of file
diff --git a/.github/workflows/review-apps.yml b/.github/workflows/review-apps.yml
deleted file mode 100644
index 5b646f73..00000000
--- a/.github/workflows/review-apps.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-name: Review App
-on:
- pull_request:
- types: [opened, reopened, synchronize, closed]
-env:
- FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
-jobs:
- review_app:
- if: github.event.pull_request.user.login != 'dependabot[bot]'
- name: Review App Job
- runs-on: ubuntu-latest
- steps:
- - name: Checkout repository
- uses: actions/checkout@v3
- - name: Install flyctl
- run: curl -L https://fly.io/install.sh | FLYCTL_INSTALL=/usr/local sh
- - name: Set up Elixir
- uses: erlef/setup-beam@v1
- with:
- otp-version: 24.3.4
- elixir-version: 1.14.1
- - name: Run Review App Script
- run: ./.github/scripts/review-apps.sh
- env:
- ENCRYPTION_KEYS: ${{ secrets. ENCRYPTION_KEYS }}
- AUTH_API_KEY: ${{ secrets.FLY_AUTH_API_KEY }}
- PR_NUMBER: ${{ github.event.number}}
- EVENT_ACTION: ${{ github.event.action }}
- FLY_ORG: dwyl-mvp
- FLY_REGION: lhr
- FLY_POSTGRES_NAME: mvp-db
-
diff --git a/build-review-apps.md b/build-review-apps.md
index 714900f4..39378e12 100644
--- a/build-review-apps.md
+++ b/build-review-apps.md
@@ -1,60 +1,130 @@
# Fly Review Apps with Github Actions
-## Setup
+In this small guide,
+we are going to be setting up
+[CI/CD](https://aws.amazon.com/devops/continuous-integration/)
+so code tests are automatically executed in every PR made to the main branch
+and to maintain code quality
+throughout the development cycle.
+
+We are going to be focusing
+on the job that **deploys the app to `fly.io`**,
+which constitutes the **"Review App"**.
-Firstly read [Understanding Github Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions).
+- [Fly Review Apps with Github Actions](#fly-review-apps-with-github-actions)
+ - [Setup](#setup)
+ - [Workflow](#workflow)
+ - [Create script](#create-script)
+ - [You're done! 🎉](#youre-done-)
-Now we can start to build our own Github actions to create "review apps"
-when a PR is created.
+
+## Setup
+
+Firstly,
+read [Understanding Github Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions)
+to have a better understanding of what Github Actions are
+and how to properly have them executed.
+
+Now we can start building our own Github actions
+that create **"Review Apps"** when a PR is created.
+**"Review Apps"** pertain to the code of the PR
+that is deployed automatically to
+[`fly.io`](https://fly.io/).
+With this app deployed,
+it is easy to see how different the app is
+with the changes made in the PR
+*and* is extremely useful to also run API definition tests against it.
Because we are going to use `flyctl` to create and deploy the applications
-based on the PRs, we need first to create a `fly auth token`.
+based on the PRs,
+we first need to create a `fly auth token`.
-Run `flyctl auth token`. This will create and show the token in your terminal.
-see: https://fly.io/docs/flyctl/auth-token/
+Run `flyctl auth token`.
+This will create and show the token in your terminal.
+see: https://fly.io/docs/flyctl/auth-token/.
In your Github repository, create a new secret named `FLY_API_TOKEN`
-and associate to it the token: go to `Settings` then `Secrets` and finally `Actions`.
+and associate to it the token:
+go to `Settings` then `Secrets` and finally `Actions`.
At the top right corner you should see the `New repository secret` button.
-`flyctl` will check if the `FLY_API_TOKEN` environment variable is defined, and
-use it to manage the Fly applications.
+
+
+
+`flyctl` will check if the `FLY_API_TOKEN` environment variable is defined,
+and use it to manage the Fly applications.
-## Create Workflow
+## Workflow
-Create a new [workflow](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions#workflows)
-file, for example `.github/workflows/review-apps.yml`:
+If you open the file
+[`.github/workflows/pr.yml`](./.github/workflows/pr.yml),
+you will see how the "Review App" is deployed.
+This file is executed on all the PRs
+that target the `main` branch.
```yml
-name: Review App
+name: PR CI
+
on:
pull_request:
+ branches: [ main ]
types: [opened, reopened, synchronize, closed]
-env:
- FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
+
jobs:
+
+ ...
+
+ # DEPLOY THE REVIEW APP
+ # This will deploy an app to fly.io with the name 'mvp-pr-$PR_NUMBER' (check `review-apps.sh` script).
review_app:
+
+ # Only run when it's not a dependabot PR
+ if: github.event.pull_request.user.login != 'dependabot[bot]'
+
name: Review App Job
runs-on: ubuntu-latest
+ needs: [build]
steps:
- name: Checkout repository
uses: actions/checkout@v3
+
- name: Install flyctl
run: curl -L https://fly.io/install.sh | FLYCTL_INSTALL=/usr/local sh
+
+ - name: Set up Elixir
+ uses: erlef/setup-beam@v1
+ with:
+ otp-version: 24.3.4
+ elixir-version: 1.14.1
+
- name: Run Review App Script
- run: ./.github/scripts/review-app.sh
+ run: ./.github/scripts/review-apps.sh
+ env:
+ ENCRYPTION_KEYS: ${{ secrets. ENCRYPTION_KEYS }}
+ AUTH_API_KEY: ${{ secrets.FLY_AUTH_API_KEY }}
+ APP_NAME: ${{ format('mvp-pr-{0}', github.event.number) }}
+ EVENT_ACTION: ${{ github.event.action }}
+ FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
+ FLY_ORG: dwyl-mvp
+ FLY_REGION: lhr
+ FLY_POSTGRES_NAME: mvp-db
+
+ ...
```
-- Trigger this Github action when a PR is created, updated, reopened or closed:
+- The Github Action is triggered when a PR is created, updated, reopened or closed:
```yml
on:
pull_request:
+ branches: [ main ]
types: [opened, reopened, synchronize, closed]
```
-- Add the environment variable to the workflow. The `FLY_API_TOKEN` is set
-from the Github repository secrets. This environment is used by `flyctl` to
+- The environment variable is added to the workflow.
+The `FLY_API_TOKEN` is set
+from the Github repository secrets.
+This environment is used by `flyctl` to
authenticate the requests to Fly.io.
Learn more about Github action environment variables:
@@ -65,34 +135,48 @@ env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
```
-- Start a new `job` with a runner based on the latest ubuntu:
+- Start a new `job` with a runner based on the latest ubuntu.
+The job only runs after the `build` job
+and not when the [dependabot](https://github.com/dependabot) creates a PR.
```yml
jobs:
+
+ ...
review_app:
+
+ # Only run when it's not a dependabot PR
+ if: github.event.pull_request.user.login != 'dependabot[bot]'
+
name: Review App Job
runs-on: ubuntu-latest
+ needs: [build]
+ steps:
```
-- Finally define the steps of the job.
+- Defining the rest of the steps of the job
-First we checkout the repository to the
-runner using the `actions/checkout`. This allow the runner to access the files
-in the repository. `flyctl` needs to be able to read the `Dockerfile` and the `fly.toml`
-to build the application but we also need our runner to be able to access the shell
-script defines in the last steps.
+First,
+we checkout the repository to the
+runner using the `actions/checkout`.
+This allows the runner to access the files in the repository.
+`flyctl` needs to be able to read the `Dockerfile` and the `fly.toml`
+to build the application,
+but we also need our runner to be able to access
+the shell script defined in the last step.
-The second steps is to install `flyctl`: `curl -L https://fly.io/install.sh | FLYCTL_INSTALL=/usr/local sh`
-This command download `flyctl` and install it in `/usr/local`. The `FLYCTL_INSTALL`
-environment variable is used in the `install.sh` script to define where to install
-`flyctl`, see:
-https://github.com/superfly/flyctl/blob/74443a0696e3c3d7d8dbbec6feded3dc928e251f/installers/install.sh#L17
+The second step is to install `flyctl`: `curl -L https://fly.io/install.sh | FLYCTL_INSTALL=/usr/local sh`
+This command downloads `flyctl` and installs it in `/usr/local`.
+The `FLYCTL_INSTALL` environment variable
+is used in the `install.sh` script to define where to install `flyctl`,
+see: https://github.com/superfly/flyctl/blob/74443a0696e3c3d7d8dbbec6feded3dc928e251f/installers/install.sh#L17
-> /usr/local is used by the system administrator when installing software locally
+> `/usr/local` is used by the system administrator when installing software locally.
-The third and final step is to run the shell script that we're going to define next.
-This script will contain all the required `flyctl` command calls to create and deploy
-our review applications.
+The third and final step is to run the shell script
+that we're going to define next.
+This script will contain all the required `flyctl` command calls
+to create and deploy our "review apps".
```yml
- name: Run Review App Script
@@ -110,12 +194,11 @@ steps:
run: ./.github/scripts/review-app.sh
```
-refs:
-- [Using jobs in a workflow](https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow)
+ref: [Using jobs in a workflow](https://docs.github.com/en/actions/using-jobs/using-jobs-in-a-workflow)
-# Create script
+## Create script
-This script take inspiration from: https://github.com/superfly/fly-pr-review-apps/blob/main/entrypoint.sh
+This script takes inspiration from: https://github.com/superfly/fly-pr-review-apps/blob/main/entrypoint.sh
The first step is to create the script file `.github/scripts/review-app.sh` and
make it executable by running `chmod +x .github/scripts/review-app.sh`
@@ -127,7 +210,7 @@ set -e
echo "Review App Script"
# create "unique" name for fly review app
-app="mvp-pr-$PR_NUMBER"
+app=$APP_NAME
secrets="AUTH_API_KEY=$AUTH_API_KEY ENCRYPTION_KEYS=$ENCRYPTION_KEYS"
if [ "$EVENT_ACTION" = "closed" ]; then
@@ -166,7 +249,7 @@ You can see the documentation with `help set`:
variables we're going to add to the Fly application:
```sh
-app="mvp-pr-$PR_NUMBER"
+app=$APP_NAME
secrets="AUTH_API_KEY=$AUTH_API_KEY ENCRYPTION_KEYS=$ENCRYPTION_KEYS"
```
@@ -187,12 +270,12 @@ If it doesn't, we first create the application with:
flyctl launch --no-deploy --copy-config --name "$app" --region "$FLY_REGION" --org "$FLY_ORG" --remote-only
```
---no-deploy to not deploy the application yet as we need to set environment variables
+- `--no-deploy `to not deploy the application yet as we need to set environment variables
and attach the database to it.
---copy-config, use the existing configuration file.
+- `--copy-config`, use the existing configuration file.
-See more documentation for launch: https://fly.io/docs/flyctl/launch/
+See more documentation for launch: https://fly.io/docs/flyctl/launch/.
The next step is to attach the existing postgres cluster to the new application with:
@@ -210,50 +293,63 @@ The `tr` command replaces the spaces by new lines. The values are then piped
to the `flyctl secrets inport` command.
-Finally we deploy the application:
+Finally we deploy the application!
```sh
flyctl deploy --app "$app" --region "$FLY_REGION" --strategy immediate
```
-see deploy doc: https://fly.io/docs/flyctl/deploy/
+see deploy doc: https://fly.io/docs/flyctl/deploy/.
-> --strategy string
-The strategy for replacing running instances. Options are canary, rolling, bluegreen,
-or immediate. Default is canary, or rolling when max-per-region is set.
+> `--strategy string`
+The strategy for replacing running instances.
+Options are `canary`, `rolling`, bl`uegreen,
+or `immediate`.
+Default is `canary`, or `rolling` when max-per-region is set.
If the application already exists we only use the `deploy` command as we don't
-need to `launch`, set environment variables or attach the postgres cluster.
+need to `launch`, set environment variables or attach the Postgres cluster.
-For new created applications a new empty database and user for the database will
-be created.
+For newly created applications,
+a new empty database and user for the database will be created.
-When PRs are merged/closed the Fly applications will be `destroyed`, however
-the database and user will still be present in the cluster.
+When PRs are merged/closed, the Fly apps will be **destroyed**.
+However, the database and user will still be present in the cluster.
-To remove them run:
+To remove them, run:
```sh
flyctl postgres connect -a
```
-Once connected in psql run:
+Once connected, in `psql`, run:
```sh
drop database with (force);
drop user ;
```
-To see existing databases and users (roles):
+## You're done! 🎉
-```sh
-# \l
-# \du
-```
+Now you know how to create a job
+that uses a *script* to deploy
+a "Review App"!
+
+If you take a look at the
+rest of
+[`.github/workflows/pr.yml`](./.github/workflows/pr.yml),
+you will notice there are two other jobs:
+
+- **`build`**: builds and runs the unit tests
+of the Elixir app.
+It also checks if code is formatted.
+If executed successfully,
+the code coverage is pushed to `Codecov`.
+- **`api_definition`**: runs API definition tests.
+It *must be executed **after*** the `review_app` job,
+because it makes requests to the deployed "Review App".
-\l list the databases
-\du list the users
diff --git a/lib/api/MVP.json b/lib/api/MVP.json
index 83f721ed..74969983 100644
--- a/lib/api/MVP.json
+++ b/lib/api/MVP.json
@@ -1,97 +1,65 @@
[
{
+ "v": 1,
+ "requests": [],
"folders": [
{
- "folders": [],
- "name": "Items",
"v": 1,
+ "name": "Items",
"requests": [
{
- "params": [],
- "endpoint": "<>/api/items/<>",
- "headers": [
- {
- "active": true,
- "key": "accept",
- "value": "application/json"
- }
- ],
- "preRequestScript": "",
"body": {
- "body": null,
- "contentType": null
+ "contentType": "application/json",
+ "body": "{\n \"text\": \"aaa text\"\n}"
},
- "v": "1",
- "testScript": "// Check status code is 2xx\npw.test(\"Status code is 2xx\", ()=> {\n pw.expect(pw.response.status).toBeLevel2xx();\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.status).toBeType(\"number\");\n});",
- "name": "Get item",
+ "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n\n // Setting `item_id` to the response value \n \tpw.env.set(\"item_id\", pw.response.body.id.toString());\n});",
+ "preRequestScript": "",
"auth": {
"authActive": true,
- "value": "",
- "addTo": "Headers",
- "key": "",
"authType": "none"
},
- "method": "GET"
- },
- {
+ "endpoint": "<>/api/items",
+ "name": "Create item",
"headers": [
{
+ "active": true,
"value": "application/json",
- "key": "accept",
- "active": true
+ "key": "accept"
}
],
- "method": "GET",
- "preRequestScript": "",
+ "v": "1",
+ "params": [],
+ "method": "POST"
+ },
+ {
"auth": {
"authType": "none",
"authActive": true
},
- "params": [],
- "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});",
- "name": "Get item (404 - Item not found)",
- "endpoint": "<>/api/items/<>",
- "v": "1",
- "body": {
- "contentType": null,
- "body": null
- }
- },
- {
+ "endpoint": "<>/api/items",
"body": {
- "body": null,
- "contentType": null
+ "contentType": "application/json",
+ "body": "{\n \"invalid\": \"something\"\n}"
},
- "params": [],
- "preRequestScript": "",
- "method": "GET",
- "name": "Get item (400 - Invalid ID)",
- "endpoint": "<>/api/items/<>",
"testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
+ "preRequestScript": "",
+ "method": "POST",
+ "params": [],
"headers": [
{
+ "active": true,
"value": "application/json",
- "key": "accept",
- "active": true
+ "key": "accept"
}
],
- "v": "1",
- "auth": {
- "authType": "none",
- "authActive": true
- }
+ "name": "Create item (400 - Invalid attributes)",
+ "v": "1"
},
{
+ "preRequestScript": "const currentTime = Date.now();\npw.env.set(\"tag_text_to_be_repeated\", currentTime.toString() + \"_tagtext\");",
+ "params": [],
+ "endpoint": "<>/api/items",
"v": "1",
- "endpoint": "<>/api/items/<>",
- "name": "Get item with tag embed",
- "params": [
- {
- "key": "embed",
- "value": "tag",
- "active": true
- }
- ],
"headers": [
{
"value": "application/json",
@@ -99,52 +67,79 @@
"key": "accept"
}
],
- "method": "GET",
"auth": {
- "value": "",
"authType": "none",
- "authActive": true,
- "key": "",
- "addTo": "Headers"
+ "authActive": true
},
- "preRequestScript": "",
- "testScript": "// Check status code is 2xx\npw.test(\"Status code is 2xx\", ()=> {\n pw.expect(pw.response.status).toBeLevel2xx();\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.status).toBeType(\"number\");\n pw.expect(pw.response.body.tags).not.toBeType(\"undefined\");\n});",
+ "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});",
+ "name": "Create item with tags",
+ "method": "POST",
"body": {
- "contentType": null,
- "body": null
+ "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"text\": \"<>\",\n \"color\": \"#FFFFFF\"\n }\n ]\n}",
+ "contentType": "application/json"
}
},
{
+ "name": "Create item with tags (400 - Tag already exists)",
"headers": [
{
- "key": "accept",
"value": "application/json",
- "active": true
+ "active": true,
+ "key": "accept"
}
],
- "method": "POST",
+ "v": "1",
+ "preRequestScript": "",
"body": {
"contentType": "application/json",
- "body": "{\n \"text\": \"aaa text\"\n}"
+ "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"text\": \"<>\",\n \"color\": \"#FFFFFF\"\n }\n ]\n}"
},
- "v": "1",
- "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});",
+ "method": "POST",
+ "auth": {
+ "authActive": true,
+ "authType": "none"
+ },
+ "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
+ "endpoint": "<>/api/items",
+ "params": []
+ },
+ {
+ "body": {
+ "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"text\": \"yet another tag\",\n \"color\": \"#FFFFFF\"\n }\n ]\n}",
+ "contentType": "application/json"
+ },
+ "method": "POST",
+ "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
"endpoint": "<>/api/items",
- "params": [],
"preRequestScript": "",
"auth": {
"authType": "none",
"authActive": true
},
- "name": "Create item"
+ "v": "1",
+ "headers": [
+ {
+ "key": "accept",
+ "value": "application/json",
+ "active": true
+ }
+ ],
+ "params": [],
+ "name": "Create item with tags (400 - Tag is malformed)"
},
{
- "body": {
- "contentType": "application/json",
- "body": "{\n \"invalid\": \"something\"\n}"
+ "name": "Create item with tags (400 - Tag is malformed)",
+ "auth": {
+ "authType": "none",
+ "authActive": true
},
- "name": "Create item (400 - Invalid attributes)",
+ "v": "1",
+ "params": [],
"endpoint": "<>/api/items",
+ "body": {
+ "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"invalid\": \"yet another tag\"\n }\n ]\n}",
+ "contentType": "application/json"
+ },
"headers": [
{
"active": true,
@@ -154,192 +149,206 @@
],
"method": "POST",
"preRequestScript": "",
- "auth": {
- "authActive": true,
- "authType": "none"
- },
- "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
- "params": [],
- "v": "1"
+ "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});"
},
{
- "v": "1",
+ "body": {
+ "contentType": null,
+ "body": null
+ },
+ "testScript": "// Check status code is 2xx\npw.test(\"Status code is 2xx\", ()=> {\n pw.expect(pw.response.status).toBeLevel2xx();\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.status).toBeType(\"number\");\n});",
+ "params": [],
+ "auth": {
+ "value": "",
+ "addTo": "Headers",
+ "key": "",
+ "authType": "none",
+ "authActive": true
+ },
"preRequestScript": "",
+ "endpoint": "<>/api/items/<>",
"headers": [
{
- "value": "application/json",
"key": "accept",
+ "value": "application/json",
"active": true
}
],
- "endpoint": "<>/api/items",
+ "method": "GET",
+ "name": "Get item",
+ "v": "1"
+ },
+ {
"body": {
- "contentType": "application/json",
- "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"text\": \"yet another tag\",\n \"color\": \"#FFFFFF\"\n }\n ]\n}"
+ "body": null,
+ "contentType": null
},
- "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});",
- "method": "POST",
- "name": "Create item with tags",
- "params": [],
+ "method": "GET",
+ "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});",
"auth": {
"authActive": true,
"authType": "none"
- }
- },
- {
- "name": "Create item with tags (400 - Tag already exists)",
+ },
+ "name": "Get item (404 - Item not found)",
"headers": [
{
- "value": "application/json",
"key": "accept",
- "active": true
+ "active": true,
+ "value": "application/json"
}
],
- "endpoint": "<>/api/items",
- "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
- "params": [],
- "method": "POST",
- "auth": {
- "authType": "none",
- "authActive": true
- },
- "preRequestScript": "",
+ "endpoint": "<>/api/items/<>",
"v": "1",
- "body": {
- "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"text\": \"yet another tag\",\n \"color\": \"#FFFFFF\"\n }\n ]\n}",
- "contentType": "application/json"
- }
+ "preRequestScript": "",
+ "params": []
},
{
- "endpoint": "<>/api/items",
- "name": "Create item with tags (400 - Tag is malformed)",
"auth": {
- "authType": "none",
- "authActive": true
+ "authActive": true,
+ "authType": "none"
},
+ "v": "1",
+ "name": "Get item (400 - Invalid ID)",
+ "params": [],
"headers": [
{
"value": "application/json",
- "key": "accept",
- "active": true
+ "active": true,
+ "key": "accept"
}
],
+ "preRequestScript": "",
"testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
- "params": [],
- "method": "POST",
- "v": "1",
+ "method": "GET",
+ "endpoint": "<>/api/items/<>",
"body": {
- "contentType": "application/json",
- "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"text\": \"yet another tag\",\n \"color\": \"#FFFFFF\"\n }\n ]\n}"
- },
- "preRequestScript": ""
+ "body": null,
+ "contentType": null
+ }
},
{
- "name": "Create item with tags (400 - Tag is malformed)",
+ "endpoint": "<>/api/items/<>",
+ "body": {
+ "contentType": null,
+ "body": null
+ },
+ "testScript": "// Check status code is 2xx\npw.test(\"Status code is 2xx\", ()=> {\n pw.expect(pw.response.status).toBeLevel2xx();\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.status).toBeType(\"number\");\n pw.expect(pw.response.body.tags).not.toBeType(\"undefined\");\n});",
+ "auth": {
+ "authType": "none",
+ "authActive": true,
+ "addTo": "Headers",
+ "key": "",
+ "value": ""
+ },
+ "method": "GET",
+ "v": "1",
+ "name": "Get item with tag embed",
"preRequestScript": "",
- "method": "POST",
- "params": [],
- "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
- "headers": [
+ "params": [
{
- "value": "application/json",
- "key": "accept",
+ "key": "embed",
+ "value": "tag",
"active": true
}
],
- "body": {
- "contentType": "application/json",
- "body": "{\n \"text\": \"some text\",\n \"tags\": [\n {\n \"invalid\": \"yet another tag\"\n }\n ]\n}"
- },
- "endpoint": "<>/api/items",
- "v": "1",
- "auth": {
- "authType": "none",
- "authActive": true
- }
+ "headers": [
+ {
+ "key": "accept",
+ "active": true,
+ "value": "application/json"
+ }
+ ]
},
{
- "params": [],
+ "method": "PUT",
+ "body": {
+ "body": "{\n \"text\": \"new updated text\"\n}",
+ "contentType": "application/json"
+ },
"auth": {
"authType": "none",
"authActive": true
},
- "name": "Update item",
"endpoint": "<>/api/items/<>",
"headers": [
{
- "value": "application/json",
+ "key": "accept",
"active": true,
- "key": "accept"
+ "value": "application/json"
}
],
- "body": {
- "body": "{\n \"text\": \"new updated text\"\n}",
- "contentType": "application/json"
- },
+ "preRequestScript": "",
"testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.status).toBeType(\"number\");\n});",
- "method": "PUT",
"v": "1",
- "preRequestScript": ""
+ "params": [],
+ "name": "Update item"
},
{
+ "endpoint": "<>/api/items/<>",
+ "preRequestScript": "",
+ "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});",
"method": "PUT",
- "headers": [
- {
- "active": true,
- "key": "accept",
- "value": "application/json"
- }
- ],
"auth": {
"authActive": true,
"authType": "none"
},
- "v": "1",
"body": {
"body": "{\n \"text\": \"new updated text\"\n}",
"contentType": "application/json"
},
- "preRequestScript": "",
"params": [],
+ "v": "1",
"name": "Update item (404 - Item not found)",
- "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});",
- "endpoint": "<>/api/items/<>"
+ "headers": [
+ {
+ "value": "application/json",
+ "key": "accept",
+ "active": true
+ }
+ ]
}
- ]
+ ],
+ "folders": []
},
{
+ "v": 1,
+ "folders": [],
+ "name": "Timers",
"requests": [
{
- "endpoint": "<>/api/items/<>/timers",
- "v": "1",
- "params": [],
- "name": "Get timers",
- "preRequestScript": "",
- "method": "GET",
+ "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n\n \t// Setting `item_id` to the response value \n \tpw.env.set(\"item_id\", pw.response.body.id.toString());\n});\n",
"headers": [
{
+ "value": "application/json",
"key": "accept",
- "active": true,
- "value": "application/json"
+ "active": true
}
],
+ "preRequestScript": "",
"body": {
- "contentType": null,
- "body": null
+ "body": "{\n \"text\": \"aaa text\"\n}",
+ "contentType": "application/json"
},
- "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});",
+ "name": "Create item",
+ "v": "1",
+ "method": "POST",
"auth": {
- "authActive": true,
- "authType": "none"
- }
+ "authType": "none",
+ "authActive": true
+ },
+ "endpoint": "<>/api/items",
+ "params": []
},
{
- "preRequestScript": "",
+ "body": {
+ "contentType": "application/json",
+ "body": "{\n \"start\": \"2023-01-11T17:40:44\"\n}"
+ },
"auth": {
"authActive": true,
"authType": "none"
},
+ "name": "Create timer (custom start)",
"headers": [
{
"value": "application/json",
@@ -347,78 +356,71 @@
"active": true
}
],
+ "preRequestScript": "",
+ "v": "1",
+ "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n \t\n \t// Setting `timer_id` to the response value \n \tpw.env.set(\"timer_id\", pw.response.body.id.toString());\n});",
+ "endpoint": "<>/api/items/<>/timers",
+ "method": "POST",
+ "params": []
+ },
+ {
"params": [],
+ "preRequestScript": "",
+ "name": "Create timer (no start)",
"v": "1",
+ "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});\n\n\n// Set an \"timer_to_stop\" env variable\npw.env.set(\"timer_id_to_stop\", pw.response.body.id.toString());\n",
"body": {
- "body": null,
- "contentType": null
+ "contentType": null,
+ "body": null
},
- "name": "Get timer",
- "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});",
- "endpoint": "<>/api/items/<>/timers/<>",
- "method": "GET"
- },
- {
"headers": [
{
- "active": true,
"value": "application/json",
+ "active": true,
"key": "accept"
}
],
- "name": "Get timer (404 - Timer not found)",
- "preRequestScript": "",
"auth": {
"authType": "none",
"authActive": true
},
- "v": "1",
- "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});",
- "body": {
- "body": null,
- "contentType": null
- },
- "method": "GET",
- "endpoint": "<>/api/items/<>/timers/<>",
- "params": []
+ "method": "POST",
+ "endpoint": "<>/api/items/<>/timers"
},
{
+ "params": [],
"preRequestScript": "",
"body": {
- "contentType": null,
- "body": null
+ "body": "{\n \"start\": \"2023-01-11T17:40:44\",\n \"stop\": \"2023-01-11T17:40:40\"\n}",
+ "contentType": "application/json"
},
- "endpoint": "<>/api/items/<>/timers/<>",
- "method": "GET",
- "params": [],
- "name": "Get timer (400 - Invalid ID)",
"testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
- "v": "1",
+ "method": "POST",
"auth": {
- "authActive": true,
- "authType": "none"
+ "authType": "none",
+ "authActive": true
},
+ "v": "1",
"headers": [
{
- "key": "accept",
"active": true,
+ "key": "accept",
"value": "application/json"
}
- ]
+ ],
+ "name": "Create timer (400 - Stop is after start) ",
+ "endpoint": "<>/api/items/<>/timers"
},
{
- "auth": {
- "authActive": true,
- "authType": "none"
- },
- "method": "POST",
- "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});",
"body": {
"contentType": "application/json",
- "body": "{\n \"start\": \"2023-01-11T17:40:44\"\n}"
+ "body": "{\n \"start\": \"2023-invalid-01\"\n}"
},
- "name": "Create timer (custom start)",
- "preRequestScript": "",
+ "params": [],
+ "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
+ "name": "Create timer (400 - Invalid date format) ",
+ "v": "1",
+ "endpoint": "<>/api/items/<>/timers",
"headers": [
{
"key": "accept",
@@ -426,88 +428,121 @@
"active": true
}
],
- "endpoint": "<>/api/items/<>/timers",
- "params": [],
- "v": "1"
+ "method": "POST",
+ "auth": {
+ "authType": "none",
+ "authActive": true
+ },
+ "preRequestScript": ""
},
{
- "params": [],
- "v": "1",
+ "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});",
+ "preRequestScript": "",
"headers": [
{
+ "value": "application/json",
"active": true,
- "key": "accept",
- "value": "application/json"
+ "key": "accept"
}
],
- "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});",
+ "endpoint": "<>/api/items/<>/timers",
+ "body": {
+ "body": null,
+ "contentType": null
+ },
+ "name": "Get timers",
+ "params": [],
+ "method": "GET",
+ "v": "1",
"auth": {
"authType": "none",
"authActive": true
- },
- "name": "Create timer (no start)",
- "preRequestScript": "",
- "endpoint": "<>/api/items/<>/timers",
- "body": {
- "contentType": null,
- "body": null
- },
- "method": "POST"
+ }
},
{
- "body": {
- "contentType": "application/json",
- "body": "{\n \"start\": \"2023-01-11T17:40:44\",\n \"stop\": \"2023-01-11T17:40:40\"\n}"
- },
"preRequestScript": "",
+ "method": "GET",
+ "params": [],
+ "endpoint": "<>/api/items/<>/timers/<>",
"auth": {
"authType": "none",
"authActive": true
},
- "v": "1",
- "params": [],
- "method": "POST",
- "endpoint": "<>/api/items/<>/timers",
+ "name": "Get timer",
+ "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});",
"headers": [
{
- "key": "accept",
"active": true,
+ "key": "accept",
"value": "application/json"
}
],
- "name": "Create timer (400 - Stop is after start) ",
- "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});"
+ "body": {
+ "contentType": null,
+ "body": null
+ },
+ "v": "1"
},
{
+ "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});",
"params": [],
+ "method": "GET",
+ "endpoint": "<>/api/items/<>/timers/<>",
+ "preRequestScript": "",
"body": {
- "contentType": "application/json",
- "body": "{\n \"start\": \"2023-invalid-01\"\n}"
+ "contentType": null,
+ "body": null
},
"auth": {
"authType": "none",
"authActive": true
},
- "endpoint": "<>/api/items/<>/timers",
- "name": "Create timer (400 - Invalid date format) ",
- "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
- "method": "POST",
"headers": [
{
- "key": "accept",
"active": true,
+ "key": "accept",
"value": "application/json"
}
],
- "preRequestScript": "",
+ "name": "Get timer (404 - Timer not found)",
"v": "1"
},
{
- "endpoint": "<>/api/items/<>/timers/<>",
+ "preRequestScript": "",
+ "v": "1",
+ "headers": [
+ {
+ "key": "accept",
+ "active": true,
+ "value": "application/json"
+ }
+ ],
+ "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
+ "endpoint": "<>/api/items/<>/timers/<>",
"body": {
- "body": "{\n \"start\": \"2023-01-11T17:40:44\",\n \"stop\": \"2023-01-11T17:40:48\"\n}",
- "contentType": "application/json"
+ "contentType": null,
+ "body": null
+ },
+ "method": "GET",
+ "name": "Get timer (400 - Invalid ID)",
+ "auth": {
+ "authType": "none",
+ "authActive": true
+ },
+ "params": []
+ },
+ {
+ "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});",
+ "endpoint": "<>/api/timers/<>",
+ "v": "1",
+ "preRequestScript": "",
+ "params": [],
+ "body": {
+ "body": null,
+ "contentType": null
},
+ "method": "PUT",
+ "name": "Stop timer",
"headers": [
{
"value": "application/json",
@@ -515,31 +550,22 @@
"key": "accept"
}
],
- "name": "Update timer",
- "params": [],
- "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.start).toBeType(\"string\");\n pw.expect(pw.response.body.stop).toBeType(\"string\");\n});",
- "v": "1",
"auth": {
"authActive": true,
"authType": "none"
- },
- "preRequestScript": "",
- "method": "PUT"
+ }
},
{
"v": "1",
- "endpoint": "<>/api/items/<>/timers/<>",
- "params": [],
+ "endpoint": "<>/api/timers/<>",
"body": {
- "body": "{\n \"start\": \"2023-invalid-01\"\n}",
- "contentType": "application/json"
+ "contentType": null,
+ "body": null
},
"auth": {
- "authActive": true,
- "authType": "none"
+ "authType": "none",
+ "authActive": true
},
- "preRequestScript": "",
- "name": "Update timer (400 - Invalid date format)",
"headers": [
{
"active": true,
@@ -547,150 +573,129 @@
"value": "application/json"
}
],
- "method": "PUT",
- "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});"
+ "name": "Stop timer (403 - Timer has already been stopped)",
+ "params": [],
+ "testScript": "// Check status code is 403\npw.test(\"Status code is 403\", ()=> {\n pw.expect(pw.response.status).toBe(403);\n});",
+ "preRequestScript": "",
+ "method": "PUT"
},
{
- "endpoint": "<>/api/items/<>/timers/<>",
- "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
+ "name": "Stop timer (404 - Timer not found)",
"v": "1",
- "body": {
- "body": "{\n \"start\": \"2023-01-11T17:40:44\",\n \"stop\": \"2023-01-11T17:40:40\"\n}",
- "contentType": "application/json"
- },
"headers": [
{
"key": "accept",
- "value": "application/json",
- "active": true
+ "active": true,
+ "value": "application/json"
}
],
- "params": [],
+ "method": "PUT",
"auth": {
"authType": "none",
"authActive": true
},
- "name": "Update timer (400 - Stop is after start)",
- "method": "PUT",
- "preRequestScript": ""
+ "preRequestScript": "",
+ "params": [],
+ "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});",
+ "endpoint": "<>/api/timers/<>",
+ "body": {
+ "contentType": null,
+ "body": null
+ }
},
{
- "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});",
+ "method": "PUT",
+ "preRequestScript": "",
+ "auth": {
+ "authActive": true,
+ "authType": "none"
+ },
"body": {
- "body": null,
- "contentType": null
+ "contentType": "application/json",
+ "body": "{\n \"start\": \"2023-01-11T17:40:44\",\n \"stop\": \"2023-01-11T17:40:48\"\n}"
},
- "preRequestScript": "",
- "endpoint": "<>/api/timers/<>",
- "v": "1",
- "method": "PUT",
"params": [],
- "name": "Stop timer",
+ "endpoint": "<>/api/items/<>/timers/<>",
+ "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.start).toBeType(\"string\");\n pw.expect(pw.response.body.stop).toBeType(\"string\");\n});",
"headers": [
{
+ "value": "application/json",
"active": true,
- "key": "accept",
- "value": "application/json"
+ "key": "accept"
}
],
- "auth": {
- "authActive": true,
- "authType": "none"
- }
+ "name": "Update timer",
+ "v": "1"
},
{
- "name": "Stop timer (403 - Timer has already been stopped)",
- "params": [],
- "endpoint": "<>/api/timers/<>",
- "preRequestScript": "",
- "method": "PUT",
- "body": {
- "contentType": null,
- "body": null
- },
"headers": [
{
- "active": true,
"key": "accept",
+ "active": true,
"value": "application/json"
}
],
- "testScript": "\n\n// Check status code is 403\npw.test(\"Status code is 403\", ()=> {\n pw.expect(pw.response.status).toBe(403);\n});",
+ "name": "Update timer (400 - Invalid date format)",
+ "params": [],
+ "preRequestScript": "",
+ "body": {
+ "body": "{\n \"start\": \"2023-invalid-01\"\n}",
+ "contentType": "application/json"
+ },
"v": "1",
+ "endpoint": "<>/api/items/<>/timers/<>",
+ "method": "PUT",
+ "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
"auth": {
"authType": "none",
"authActive": true
}
},
{
- "method": "PUT",
"v": "1",
- "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});",
- "name": "Stop timer (404 - Timer not found)",
"body": {
- "contentType": null,
- "body": null
+ "contentType": "application/json",
+ "body": "{\n \"start\": \"2023-01-11T17:40:44\",\n \"stop\": \"2023-01-11T17:40:40\"\n}"
},
+ "name": "Update timer (400 - Stop is after start)",
+ "preRequestScript": "",
+ "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
+ "params": [],
+ "method": "PUT",
+ "endpoint": "<>/api/items/<>/timers/<>",
"headers": [
{
- "key": "accept",
"value": "application/json",
- "active": true
+ "active": true,
+ "key": "accept"
}
],
- "endpoint": "<>/api/timers/<>",
- "params": [],
- "preRequestScript": "",
"auth": {
"authActive": true,
"authType": "none"
}
}
- ],
- "folders": [],
- "v": 1,
- "name": "Timers"
+ ]
},
{
"v": 1,
- "folders": [],
+ "name": "Tags",
"requests": [
{
- "testScript": "// Check status code is 2xx\npw.test(\"Status code is 2xx\", ()=> {\n pw.expect(pw.response.status).toBeLevel2xx();\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.text).toBeType(\"string\");\n\n \t// Color must be an hex color\n pw.expect(pw.response.body.color).toBeType(\"string\");\n pw.expect(pw.response.body.color).toHaveLength(7);\n pw.expect(pw.response.body.color).toInclude(\"#\");\n \n});",
"auth": {
- "addTo": "Headers",
- "value": "",
"authActive": true,
+ "value": "",
"key": "",
+ "addTo": "Headers",
"authType": "none"
},
- "params": [],
- "endpoint": "<>/api/tags/<>",
- "name": "Get tag",
+ "name": "Create tag",
+ "method": "POST",
"v": "1",
- "method": "GET",
- "headers": [
- {
- "value": "application/json",
- "active": true,
- "key": "accept"
- }
- ],
- "body": {
- "body": null,
- "contentType": null
- },
- "preRequestScript": ""
- },
- {
+ "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n \n // Setting `tag_id` to the response value \n \tpw.env.set(\"tag_id\", pw.response.body.id.toString());\n});",
"body": {
- "body": null,
- "contentType": null
- },
- "endpoint": "<>/api/tags/<>",
- "auth": {
- "authType": "none",
- "authActive": true
+ "contentType": "application/json",
+ "body": "{\n \"text\": \"<>\",\n \"person_id\": 0,\n \"color\": \"#FFFFFF\"\n}"
},
"headers": [
{
@@ -699,187 +704,209 @@
"active": true
}
],
- "method": "GET",
- "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});",
- "preRequestScript": "",
- "name": "Get tag (404 - Tag not found)",
- "params": [],
- "v": "1"
+ "preRequestScript": "const currentTime = Date.now();\npw.env.set(\"random_string\", currentTime.toString());",
+ "endpoint": "<>/api/tags",
+ "params": []
},
{
+ "params": [],
+ "endpoint": "<>/api/tags",
"body": {
- "body": null,
- "contentType": null
+ "body": "{\n \"text\": \"<>\",\n \"person_id\": 0\n}",
+ "contentType": "application/json"
},
+ "preRequestScript": "const currentTime = Date.now();\npw.env.set(\"random_string\", currentTime.toString() + \"_nocolor\");",
+ "method": "POST",
"v": "1",
"headers": [
{
- "key": "accept",
"value": "application/json",
- "active": true
+ "active": true,
+ "key": "accept"
}
],
- "method": "GET",
+ "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});",
"auth": {
"authActive": true,
"authType": "none"
},
- "preRequestScript": "",
- "params": [],
- "endpoint": "<>/api/tags/<>",
- "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
- "name": "Get tag (400 - Invalid ID)"
+ "name": "Create tag (no color provided)"
},
{
"endpoint": "<>/api/tags",
+ "testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
+ "name": "Create tag (400 - Invalid attributes)",
+ "body": {
+ "body": "{\n \"text\": \"tag text\",\n \"color\": \"invalid color\"\n}",
+ "contentType": "application/json"
+ },
+ "params": [],
"auth": {
"authType": "none",
"authActive": true
},
+ "v": "1",
"headers": [
{
- "active": true,
+ "value": "application/json",
"key": "accept",
- "value": "application/json"
+ "active": true
}
],
- "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});",
- "body": {
- "contentType": "application/json",
- "body": "{\n \"text\": \"tag text\",\n \"person_id\": 0,\n \"color\": \"#FFFFFF\"\n}"
- },
- "preRequestScript": "",
- "v": "1",
- "params": [],
- "name": "Create tag",
- "method": "POST"
+ "method": "POST",
+ "preRequestScript": ""
},
{
- "endpoint": "<>/api/tags",
+ "params": [],
+ "preRequestScript": "",
"headers": [
{
- "value": "application/json",
"active": true,
+ "value": "application/json",
"key": "accept"
}
],
+ "testScript": "// Check status code is 2xx\npw.test(\"Status code is 2xx\", ()=> {\n pw.expect(pw.response.status).toBeLevel2xx();\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.text).toBeType(\"string\");\n\n \t// Color must be an hex color\n pw.expect(pw.response.body.color).toBeType(\"string\");\n pw.expect(pw.response.body.color).toHaveLength(7);\n pw.expect(pw.response.body.color).toInclude(\"#\");\n \n});",
+ "method": "GET",
+ "body": {
+ "body": null,
+ "contentType": null
+ },
+ "name": "Get tag",
+ "endpoint": "<>/api/tags/<>",
"v": "1",
- "method": "POST",
"auth": {
+ "addTo": "Headers",
+ "authActive": true,
+ "key": "",
"authType": "none",
- "authActive": true
+ "value": ""
+ }
+ },
+ {
+ "endpoint": "<>/api/tags/<>",
+ "v": "1",
+ "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});",
+ "body": {
+ "contentType": null,
+ "body": null
},
+ "name": "Get tag (404 - Tag not found)",
"params": [],
- "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.id).toBeType(\"number\");\n});",
- "name": "Create tag (no color provided)",
+ "method": "GET",
"preRequestScript": "",
- "body": {
- "contentType": "application/json",
- "body": "{\n \"text\": \"another tag text\",\n \"person_id\": 0\n}"
- }
+ "auth": {
+ "authType": "none",
+ "authActive": true
+ },
+ "headers": [
+ {
+ "value": "application/json",
+ "key": "accept",
+ "active": true
+ }
+ ]
},
{
+ "auth": {
+ "authActive": true,
+ "authType": "none"
+ },
+ "endpoint": "<>/api/tags/<>",
+ "name": "Get tag (400 - Invalid ID)",
"testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
+ "params": [],
"headers": [
{
+ "active": true,
"key": "accept",
- "value": "application/json",
- "active": true
+ "value": "application/json"
}
],
+ "preRequestScript": "",
"body": {
- "contentType": "application/json",
- "body": "{\n \"text\": \"tag text\",\n \"color\": \"invalid color\"\n}"
- },
- "params": [],
- "auth": {
- "authType": "none",
- "authActive": true
+ "contentType": null,
+ "body": null
},
- "preRequestScript": "",
- "name": "Create tag (400 - Invalid attributes)",
- "endpoint": "<>/api/tags",
- "method": "POST",
+ "method": "GET",
"v": "1"
},
{
- "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.text).toBeType(\"string\");\n\n \t\n \t// Color must be an hex color\n pw.expect(pw.response.body.color).toBeType(\"string\");\n pw.expect(pw.response.body.color).toHaveLength(7);\n pw.expect(pw.response.body.color).toInclude(\"#\");\n});",
- "name": "Update tag",
+ "testScript": "// Check status code is 200\npw.test(\"Status code is 200\", ()=> {\n pw.expect(pw.response.status).toBe(200);\n});\n\n\n// Check JSON response property\npw.test(\"Check JSON response property\", ()=> {\n pw.expect(pw.response.body.person_id).toBeType(\"number\");\n pw.expect(pw.response.body.text).toBeType(\"string\");\n\n \t\n// Color must be an hex color\n pw.expect(pw.response.body.color).toBeType(\"string\");\n pw.expect(pw.response.body.color).toHaveLength(7);\n pw.expect(pw.response.body.color).toInclude(\"#\");\n});",
+ "preRequestScript": "const currentTime = Date.now();\npw.env.set(\"random_string\", currentTime.toString() + \"_new_updated_string\");",
"body": {
- "body": "{\n \"text\": \"new updated tag text\"\n}",
+ "body": "{\n \"text\": \"<>\"\n}",
"contentType": "application/json"
},
- "v": "1",
"headers": [
{
"value": "application/json",
- "key": "accept",
- "active": true
+ "active": true,
+ "key": "accept"
}
],
+ "params": [],
+ "method": "PUT",
"auth": {
"authActive": true,
"authType": "none"
},
+ "v": "1",
"endpoint": "<>/api/tags/<>",
- "method": "PUT",
- "params": [],
- "preRequestScript": ""
+ "name": "Update tag"
},
{
- "preRequestScript": "",
- "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});",
"name": "Update tag (404 - Tag not found)",
+ "params": [],
+ "method": "PUT",
+ "testScript": "// Check status code is 404\npw.test(\"Status code is 404\", ()=> {\n pw.expect(pw.response.status).toBe(404);\n});",
+ "v": "1",
"headers": [
{
+ "key": "accept",
"value": "application/json",
- "active": true,
- "key": "accept"
+ "active": true
}
],
+ "preRequestScript": "",
+ "endpoint": "<>/api/tags/<>",
+ "auth": {
+ "authType": "none",
+ "authActive": true
+ },
"body": {
"contentType": "application/json",
"body": "{\n \"text\": \"new updated text\"\n}"
- },
+ }
+ },
+ {
+ "v": "1",
+ "params": [],
+ "method": "PUT",
+ "preRequestScript": "",
+ "endpoint": "<>/api/tags/<>",
"auth": {
"authType": "none",
"authActive": true
},
- "params": [],
- "method": "PUT",
- "endpoint": "<>/api/tags/<>",
- "v": "1"
- },
- {
+ "body": {
+ "body": "{\n \"text\": \"tag text\",\n \"color\": \"invalid color\"\n}",
+ "contentType": "application/json"
+ },
"name": "Update tag (400 - Invalid attributes)",
- "endpoint": "<>/api/tags/<>",
"testScript": "// Check status code is 400\npw.test(\"Status code is 400\", ()=> {\n pw.expect(pw.response.status).toBe(400);\n});",
- "preRequestScript": "",
- "v": "1",
"headers": [
{
"key": "accept",
- "active": true,
- "value": "application/json"
+ "value": "application/json",
+ "active": true
}
- ],
- "method": "PUT",
- "body": {
- "contentType": "application/json",
- "body": "{\n \"text\": \"tag text\",\n \"color\": \"invalid color\"\n}"
- },
- "auth": {
- "authType": "none",
- "authActive": true
- },
- "params": []
+ ]
}
],
- "name": "Tags"
+ "folders": []
}
],
- "name": "MVP",
- "v": 1,
- "requests": []
+ "name": "MVP"
}
]
\ No newline at end of file
diff --git a/lib/api/envs.json b/lib/api/envs.json
index d77ddc66..ac06be73 100644
--- a/lib/api/envs.json
+++ b/lib/api/envs.json
@@ -11,20 +11,20 @@
"key": "item_id"
},
{
- "value": "-1",
- "key": "notfound_item_id"
+ "key": "notfound_item_id",
+ "value": "-1"
},
{
- "key": "invalid_id",
- "value": "invalid_id"
+ "value": "invalid_id",
+ "key": "invalid_id"
},
{
- "key": "timer_id",
- "value": "1"
+ "value": "1",
+ "key": "timer_id"
},
{
- "value": "-1",
- "key": "notfound_timer_id"
+ "key": "notfound_timer_id",
+ "value": "-1"
},
{
"key": "timer_id_to_stop",
@@ -32,15 +32,23 @@
},
{
"key": "tag_id",
- "value": "1"
+ "value": "9"
},
{
"value": "-1",
"key": "notfound_tag_id"
},
{
- "value": "1",
- "key": "item_id_with_tag"
+ "key": "item_id_with_tag",
+ "value": "1"
+ },
+ {
+ "value": "1677500147391_nocolor",
+ "key": "random_string"
+ },
+ {
+ "key": "tag_text_to_be_repeated",
+ "value": "yet another tag"
}
]
}
diff --git a/lib/api/fly_dev.json b/lib/api/fly_dev.json
new file mode 100644
index 00000000..0f6c8bdb
--- /dev/null
+++ b/lib/api/fly_dev.json
@@ -0,0 +1,14 @@
+{
+ "host": "deployed_app_host",
+ "item_id": "1",
+ "notfound_item_id": "-1",
+ "invalid_id": "invalid_id",
+ "timer_id": "1",
+ "notfound_timer_id": "-1",
+ "timer_id_to_stop": "2",
+ "tag_id": "1",
+ "notfound_tag_id": "-1",
+ "item_id_with_tag": "1",
+ "random_string": "random_string",
+ "tag_text_to_be_repeated": "yet another tag"
+}
\ No newline at end of file
diff --git a/lib/api/localhost.json b/lib/api/localhost.json
index 76819a4d..db89b783 100644
--- a/lib/api/localhost.json
+++ b/lib/api/localhost.json
@@ -8,5 +8,7 @@
"timer_id_to_stop": "2",
"tag_id": "1",
"notfound_tag_id": "-1",
- "item_id_with_tag": "1"
+ "item_id_with_tag": "1",
+ "random_string": "random_string",
+ "tag_text_to_be_repeated": "yet another tag"
}
\ No newline at end of file
diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs
index 09796df7..619e26f9 100644
--- a/priv/repo/seeds.exs
+++ b/priv/repo/seeds.exs
@@ -8,28 +8,3 @@
if not Envar.is_set?("AUTH_API_KEY") do
Envar.load(".env")
end
-
-if Mix.env() == :dev do
- # Create item
- App.Item.create_item_with_tags(%{
- text: "random text",
- person_id: 0,
- status: 2,
- tags: [%{text: "first_tag", person_id: 0}]
- })
-
- # Create timers
- {:ok, _timer} =
- App.Timer.start(%{
- item_id: 1,
- start: "2023-01-19 15:52:00",
- stop: "2023-01-19 15:52:03"
- })
-
- {:ok, _timer2} =
- App.Timer.start(%{item_id: 1, start: "2023-01-19 15:55:00", stop: nil})
-
- # Create tags
- {:ok, _tag} =
- App.Tag.create_tag(%{text: "random test", person_id: 0, color: "#FFFFFF"})
-end